В чем разница между асинхронным программированием и многопоточностью?
Я думал, что они были в основном то же самое -- написание программ, которые разделяют задачи между процессорами (на машинах, которые имеют 2+ Процессоры). Тогда я читаю https://msdn.microsoft.com/en-us/library/hh191443.aspx, что говорит
асинхронные методы предназначены для неблокирующих операций. Гостей ждут
выражение в асинхронном методе не блокирует текущий поток, пока
ожидаемая задача выполняется. Вместо этого выражение подписывает остальное
метода в качестве продолжения и возвращает управление вызывающему объекту
асинхронный метод.
ключевые слова async и await не вызывают дополнительных потоков
создан. Асинхронные методы не требуют многопоточности, потому что асинхронный
метод не выполняется в собственном потоке. Метод выполняется на текущем
контекст синхронизации и использует время в потоке только тогда, когда
метод активен. Вы можете использовать Task.Бег для перемещения ЦП к
фоновый поток, но фоновый поток не помогает с процессом
это просто ожидание результатов, чтобы стать доступными.
и мне интересно, может ли кто-нибудь перевести это на английский для меня. Кажется, он проводит различие между асинхронностью (это слово?) и потоковое и подразумевают, что вы можете иметь программу, которая имеет асинхронные задачи, но не многопоточность.
теперь я понимаю идею асинхронных задач, таких как пример на стр. 467 из Джона Скита C# В Глубину, Третье Издание
async void DisplayWebsiteLength ( object sender, EventArgs e )
{
label.Text = "Fetching ...";
using ( HttpClient client = new HttpClient() )
{
Task<string> task = client.GetStringAsync("http://csharpindepth.com");
string text = await task;
label.Text = text.Length.ToString();
}
}
The async ключевое слово значит "эта функция, когда бы она ни вызывалась, не будет вызываться в контексте, в котором ее завершение требуется для всего, что после ее вызова будет вызвано."
другими словами, писать его в середине какой-то задачи
int x = 5;
DisplayWebsiteLength();
double y = Math.Pow((double)x,2000.0);
С DisplayWebsiteLength() не имеет ничего общего с x или y, вызовет DisplayWebsiteLength() для выполнения "в фоновом режиме", как
processor 1 | processor 2
-------------------------------------------------------------------
int x = 5; | DisplayWebsiteLength()
double y = Math.Pow((double)x,2000.0); |
очевидно, что это глупый пример, но я прав или я полностью запутался или что?
(кроме того, я в замешательстве о том, почему sender и e никогда не используются в теле вышеуказанной функции.)
2 ответов:
ваше непонимание чрезвычайно распространено. Многие люди учат, что многопоточность и асинхронность-это одно и то же, но это не так.
обычно помогает аналогия. Вы готовите в ресторане. Приходит заказ на яйца и тосты.
- синхронно: вы готовите яйца, затем вы готовите тост.
- асинхронный, однопоточный: вы начинаете готовить яйца и устанавливаете таймер. Вы начинаете готовить тосты и устанавливаете таймер. В то время как они вы оба готовите, вы убираете кухню. Когда таймеры уходят вы берете яйца с огня и тосты из тостера и служить им.
- асинхронный, многопоточный: вы нанимаете еще двух поваров, один для приготовления яиц и один для приготовления тостов. Теперь у вас есть проблема координации повара так, чтобы они не конфликтовали друг с другом на кухне при совместном использовании ресурсов. И вы должны им заплатить.
теперь имеет смысл, что многопоточность только одна что-то вроде асинхронности? Threading - это рабочие; асинхронность-это задачи. В многопоточных рабочих процессах вы назначаете задачи работникам. В асинхронных однопоточных рабочих процессах у вас есть график задач, где некоторые задачи зависят от результатов других; по мере завершения каждой задачи он вызывает код, который планирует следующую задачу, которая может выполняться, учитывая результаты только что завершенной задачи. Но вам (надеюсь) нужен только один работник для выполнения всех задач, а не один работник на каждого задача.
это поможет понять, что многие задачи не связаны с процессором. Для задач, связанных с процессорами, имеет смысл нанять столько рабочих (потоков), сколько есть процессоров, назначить одну задачу каждому рабочему, назначить один процессор каждому рабочему и заставить каждый процессор выполнять работу только по вычислению результата как можно быстрее. Но для задач, которые не ждут на процессоре, вам не нужно назначать работника вообще. Осталось только дождаться сообщения, что результат доступен и сделайте что-нибудь еще, пока вы ждете. Когда это сообщение прибудет, вы можете запланировать продолжение выполненной задачи в качестве следующей вещи в вашем списке дел, чтобы проверить.
Итак, давайте рассмотрим пример Джона более подробно. Что происходит?
- кто-то вызывает DisplayWebSiteLength. Кто? Нам все равно.
- он устанавливает метку, создает клиента и просит клиента принести что-то. Клиент возвращает объект, представляющий задачу извлечения чего-либо. Эта задача выполняется.
- выполняется ли это в другом потоке? Скорее всего, нет. Читайте Стивен о том, почему нет потока.
- теперь мы ждем задачи. Что происходит? Мы проверяем, завершена ли задача между тем, как мы ее создали, и мы ее ждали. Если да, то мы получаем результат и продолжаем работать. Предположим, что он еще не завершен. мы подписываем оставшуюся часть этого метода как продолжение этой задачи и возвращение.
- теперь управление вернулось к вызывающему абоненту. Что он делает? Все, что он хочет.
- теперь предположим, что задача завершается. Как он это сделал? Может быть, он работал в другом потоке, или, может быть, вызывающий объект, к которому мы только что вернулись, позволил ему работать до завершения в текущем потоке. Несмотря на это, теперь у нас есть выполненная задача.
- завершенная задача запрашивает правильный поток -- опять же, скорее всего,только нить-для продолжения задания.
- управление сразу же возвращается в метод, который мы только что оставили в точке ожидания. Теперь там и результат доступен, поэтому мы можем назначить
textи запустить остальную часть метода.Это как в моей аналогии. Кто-то просит у вас документ. Вы отправляете по почте документ и продолжаете выполнять другую работу. Когда он приходит по почте вы сигнализируете, и когда вы чувствуете, как это, вы делаете остальную часть рабочего процесса-открываете конверт, платите за доставку, что угодно. Вам не нужно нанимать другого работника, чтобы сделать все это за вас.
в браузере Javascript является отличным примером асинхронной программы, которая не имеет потоков.
вам не нужно беспокоиться о том, что несколько фрагментов кода одновременно касаются одних и тех же объектов: каждая функция завершит работу до того, как на странице будет запущен любой другой javascript.
однако, когда вы делаете что-то вроде запроса AJAX, код вообще не работает, поэтому другой javascript может отвечать на такие вещи, как события щелчка, пока этот запрос не вернется и вызывает обратный вызов, связанный с ним. Если один из этих других обработчиков событий все еще работает, когда запрос AJAX возвращается, его обработчик не будет вызван, пока они не закончат. Есть только один JavaScript "поток" работает, хотя это возможно для вас, чтобы эффективно приостановить то, что вы делали, пока у вас есть необходимая информация.
в приложениях C# то же самое происходит каждый раз, когда вы имеете дело с элементами пользовательского интерфейса-вам разрешено взаимодействовать только с элементами пользовательского интерфейса, когда вы находитесь в потоке пользовательского интерфейса. Если пользователь нажал кнопку, и вы хотите ответить, прочитав большой файл с диска, неопытный программист может сделать ошибку чтения файла в самом обработчике событий click, что приведет к тому, что приложение "замерзнет" до завершения загрузки файла, потому что ему не разрешено отвечать на любые другие события, связанные с пользовательским интерфейсом, пока этот поток не будет освобожден.
один вариант программисты могут использовать, чтобы избежать этого проблема заключается в том, чтобы создать новый поток для загрузки файла, а затем сообщить коду этого потока, что при загрузке файла ему необходимо снова запустить оставшийся код в потоке пользовательского интерфейса, чтобы он мог обновить элементы пользовательского интерфейса на основе того, что он нашел в файле. До недавнего времени этот подход был очень популярен, потому что это было то, что библиотеки C# и язык сделали легко, но это принципиально сложнее, чем это должно быть.
если вы думаете о том, что делает процессор, когда он читает файл в аппаратный / операционный системный уровень, он в основном выдает инструкцию для чтения фрагментов данных с диска в память и для попадания в операционную систему с "прерыванием", когда чтение завершено. Другими словами, чтение с диска (или любого ввода/вывода на самом деле) является по своей сути асинхронные операции. Концепция потока, ожидающего завершения ввода-вывода, - это абстракция, которую разработчики библиотеки создали, чтобы упростить Программирование. Это не необходимый.
теперь большинство операций ввода-вывода в .NET имеют соответствующий
...Async()метод, который вы можете вызвать, который возвращаетTaskпочти сразу. Вы можете добавить обратные вызовы к этомуTaskчтобы указать код, который вы хотите запустить после завершения асинхронной операции. Вы также можете указать, в каком потоке вы хотите запустить этот код, и вы можете предоставить маркер, который асинхронная операция может проверять время от времени, чтобы увидеть, если вы решили отменить асинхронную задачу, давая это возможность быстро и изящно прекратить свою работу.до
async/awaitключевые слова были добавлены, C# был гораздо более очевидным о том, как вызывается код обратного вызова, потому что эти обратные вызовы были в виде делегатов, которые вы связали с задачей. Для того, чтобы все еще дать вам преимущество использования...Async()операция, избегая при этом сложности в коде,async/awaitабстрагируется от создания этих делегатов. Но они все еще есть в скомпилированном коде.таким образом, вы можете иметь свой обработчик событий UI
awaitоперация ввода-вывода, освобождающая поток пользовательского интерфейса для выполнения других действий и более или менее автоматически возвращающаяся в поток пользовательского интерфейса после завершения чтения файла-без необходимости создавать новый поток.
Comments