Я думал, что это в основном одно и то же - написание программ, которые разделяют задачи между процессорами (на машинах с 2+ процессорами). Тогда я читаю это , в котором говорится:
Асинхронные методы предназначены для неблокирующих операций. Выражение await в асинхронном методе не блокирует текущий поток во время выполнения ожидаемой задачи. Вместо этого выражение регистрирует оставшуюся часть метода как продолжение и возвращает управление вызывающей стороне асинхронного метода.
Ключевые слова async и await не приводят к созданию дополнительных потоков. Асинхронные методы не требуют многопоточности, потому что асинхронный метод не выполняется в своем собственном потоке. Метод выполняется в текущем контексте синхронизации и использует время в потоке, только когда метод активен. Вы можете использовать Task.Run для перемещения работы, связанной с ЦП, в фоновый поток, но фоновый поток не помогает с процессом, который просто ждет, когда результаты станут доступны.
и мне интересно, может ли кто-нибудь перевести это на английский для меня. Кажется, что проводится различие между асинхронностью (это слово?) И многопоточностью, и подразумевается, что у вас может быть программа, которая имеет асинхронные задачи, но не поддерживает многопоточность.
Теперь я понимаю идею асинхронных задач, таких как пример на pg. 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();
}
}
В 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
никогда не используется в теле вышеуказанной функции.)
источник
sender
иe
предполагают, что это на самом деле обработчик событий - практически единственное место, гдеasync void
это желательно. Скорее всего, это вызывается нажатием кнопки или чем-то в этом роде, в результате чего это действие происходит совершенно асинхронно по отношению к остальной части приложения. Но все это все в одном потоке - потоке пользовательского интерфейса (с небольшим промежутком времени в потоке IOCP, который отправляет обратный вызов в поток пользовательского интерфейса).DisplayWebsiteLength
кода: вы не должны использоватьHttpClient
вusing
утверждении. При большой нагрузке код может исчерпать количество доступных сокетов, что приводит к ошибкам SocketException. Больше информации о неправильной реализации .Ответы:
Ваше недоразумение очень распространено. Многих учат, что многопоточность и асинхронность - это одно и то же, но это не так.
Обычно помогает аналогия. Вы готовите в ресторане. Приходит заказ на яйца и тосты.
Имеет ли смысл, что многопоточность - это только один вид асинхронности? Threading о рабочих; асинхронность о задачах . В многопоточных рабочих процессах вы назначаете задачи работникам. В асинхронных однопоточных рабочих процессах у вас есть график задач, где некоторые задачи зависят от результатов других; По мере выполнения каждой задачи вызывается код, который планирует следующую задачу, которая может быть запущена, с учетом результатов только что выполненной задачи. Но вам (надеюсь) нужен только один работник для выполнения всех задач, а не один работник на задачу.
Это поможет понять, что многие задачи не связаны с процессором. Для задач с привязкой к процессору имеет смысл нанять столько рабочих (потоков), сколько имеется процессоров, назначить по одной задаче каждому рабочему, назначить по одному процессору каждому рабочему, и каждый процессор выполняет только работу по вычислению результата как как можно быстрее. Но для задач, которые не ждут на процессоре, вам вообще не нужно назначать работника. Вы просто ждете сообщения о том, что результат доступен, и делаете что-то еще, пока вы ждете . Когда это сообщение прибудет, вы можете запланировать продолжение выполненной задачи в качестве следующего пункта в вашем списке дел для проверки.
Итак, давайте посмотрим на пример Джона более подробно. Что случается?
text
и запустить остальную часть метода.Это как в моей аналогии. Кто-то просит у вас документ. Вы отправляете документ по почте и продолжаете выполнять другую работу. Когда оно приходит по почте, вы получаете сигнал, и когда вам это нравится, вы выполняете остальную часть рабочего процесса - открываете конверт, оплачиваете стоимость доставки, что угодно. Вам не нужно нанимать другого работника, чтобы сделать все это для вас.
источник
Встроенный в браузер Javascript - отличный пример асинхронной программы без потоков.
Вам не нужно беспокоиться о том, что несколько фрагментов кода касаются одних и тех же объектов одновременно: каждая функция завершит работу, прежде чем любой другой javascript будет разрешен для запуска на странице.
Однако при выполнении чего-то вроде запроса AJAX код вообще не выполняется, поэтому другой javascript может реагировать на такие вещи, как события click, пока этот запрос не вернется и не вызовет обратный вызов, связанный с ним. Если один из этих других обработчиков событий все еще работает, когда возвращается запрос AJAX, его обработчик не будет вызываться, пока они не будут выполнены. Работает только одна «нить» JavaScript, хотя вы можете эффективно приостановить то, что вы делали, пока не получите необходимую информацию.
В приложениях C # то же самое происходит каждый раз, когда вы имеете дело с элементами пользовательского интерфейса - вам разрешено взаимодействовать с элементами пользовательского интерфейса только тогда, когда вы находитесь в потоке пользовательского интерфейса. Если пользователь нажал кнопку, и вы хотите ответить, прочитав большой файл с диска, неопытный программист может ошибиться, прочитав файл в самом обработчике события щелчка, что приведет к «зависанию» приложения до тех пор, пока файл завершил загрузку, потому что ему больше не разрешается отвечать на щелчки, зависания или любые другие события, связанные с пользовательским интерфейсом, пока этот поток не будет освобожден.
Один из вариантов, который могут использовать программисты, чтобы избежать этой проблемы, - это создать новый поток для загрузки файла, а затем сообщить коду этого потока, что при загрузке файла ему необходимо снова запустить оставшийся код в потоке пользовательского интерфейса, чтобы он мог обновлять элементы пользовательского интерфейса. на основании того, что он нашел в файле. До недавнего времени этот подход был очень популярен, потому что это было то, что облегчали библиотеки и язык C #, но он существенно сложнее, чем должен быть.
Если вы думаете о том, что делает процессор, когда он читает файл на уровне аппаратного обеспечения и операционной системы, он в основном дает инструкцию для чтения фрагментов данных с диска в память и для удара операционной системы «прерыванием». «когда чтение завершено. Другими словами, чтение с диска (или любого другого ввода-вывода) является асинхронной операцией. Концепция потока, ожидающего завершения ввода-вывода, - это абстракция, которую разработчики библиотеки создали для облегчения программирования. Это необязательно.
Теперь большинство операций ввода / вывода в .NET имеют соответствующий
...Async()
метод, который вы можете вызвать, который возвращаетTask
почти сразу. Вы можете добавить к нему обратные вызовы, чтобыTask
указать код, который вы хотите запустить после завершения асинхронной операции. Вы также можете указать, в каком потоке вы хотите, чтобы этот код выполнялся, и вы можете предоставить токен, который асинхронная операция может время от времени проверять, чтобы определить, решили ли вы отменить асинхронную задачу, что дает ей возможность быстро остановить ее работу. и изящно.Пока
async/await
ключевые слова не были добавлены, в C # было гораздо более очевидно, как вызывается код обратного вызова, потому что эти обратные вызовы были в форме делегатов, которые вы связали с задачей. Чтобы все еще дать вам преимущество использования...Async()
операции, избегая при этом сложности в коде,async/await
абстрагируемся от создания этих делегатов. Но они все еще есть в скомпилированном коде.Таким образом, вы можете сделать так, чтобы ваш обработчик событий
await
пользовательского интерфейса выполнял операцию ввода-вывода, освобождая поток пользовательского интерфейса для выполнения других задач и более или менее автоматически возвращаясь к потоку пользовательского интерфейса после того, как вы закончили чтение файла - без необходимости создать новую темуисточник