Поток против асинхронного выполнения. Чем отличается? - PullRequest
1 голос
/ 22 мая 2010

Я полагал, что любой вид асинхронного выполнения делает поток в невидимой области. Но если это так,

  • Асинхронные коды не дают никакого выигрыша в производительности, чем многопоточные.

Но я не могу понять, почему так много разработчиков делают асинхронную форму с множеством функций. Не могли бы вы объяснить разницу и стоимость их?

Ответы [ 4 ]

6 голосов
/ 22 мая 2010

Целью асинхронного выполнения является предотвращение блокировки кода, вызывающего асинхронный метод (код переднего плана). Это позволяет коду основного плана продолжать выполнять полезную работу, пока асинхронный поток выполняет Ваша запрошенная работа в фоновом режиме. Без асинхронного выполнения код переднего плана должен дождаться завершения фоновой задачи, прежде чем он сможет продолжить выполнение.

Стоимость асинхронного выполнения такая же, как и у любой другой задачи, выполняемой в потоке.

Как правило, асинхронный объект результата регистрируется с кодом переднего плана. Объект асинхронного результата может либо вызвать событие, когда фоновая задача завершена, либо код переднего плана может периодически проверять объект результата асинхронного поиска, чтобы увидеть, установлен ли его флаг завершения.

2 голосов
/ 14 октября 2015

Для параллелизма необязательно требуются потоки.

Например, в Linux вы можете выполнять неблокирующие системные вызовы. Используя этот тип вызовов, вы можете, например, запустить несколько сетевых чтений. Ваш код может отслеживать чтения вручную (используя дескрипторы в списке и т. П.) И периодически запрашивать ОС, доступны ли новые данные по любому из подключений. Внутри ОС также хранит список текущих чтений. Используя эту технику, вы можете достичь параллелизма без каких-либо (дополнительных) потоков ни в вашей программе, ни в ОС.

Если вы используете потоки и блокируете IO, вы обычно запускаете один поток на чтение. В этом случае ОС вместо этого будет иметь список текущих потоков, которые она паркует, когда протектор пытается прочитать данные, когда их нет. Потоки возобновляются по мере поступления данных.

Переключение ОС между потоками может потребовать немного больше ресурсов в виде переключения контекста - переключения счетчика программ и регистрации содержимого. Но реальным прерывателем сделки обычно является распределение стека на поток. Этот размер по умолчанию составляет пару мегабайт в Linux. Если в вашей программе много параллелизма, это может подтолкнуть вас к использованию неблокирующих вызовов для обработки большего количества параллелизма на поток.

Таким образом, можно выполнять асинхронное программирование без потоков. Если вы хотите выполнять асинхронное программирование, используя только блокировку вызовов ОС, вам нужно выделить поток для блокировки во время продолжения. Но если вы используете неблокирующие вызовы, вы можете выполнять множество параллельных действий с помощью одного потока. Взгляните на Node.js, который отлично поддерживает множество одновременных подключений и в то же время является однопоточным для большинства операций.

Также ознакомьтесь с Голангом, который достигает подобного эффекта, используя своего рода зеленые нити, называемые горутинами. Несколько программ выполняются одновременно в одном и том же потоке ОС, и они являются ограничивающими в стековой памяти, что значительно увеличивает предел.

1 голос
/ 22 мая 2010
  • Асинхронные коды не дают никакого выигрыша в производительности, чем многопоточные.

Асинхронное выполнение - одна из черт многопоточного выполнения, которая становится все более актуальной, поскольку процессоры упаковываются в большее количество ядер.

Для серверов, многоядерные только незначительно релевантны, так как они уже написаны с учетом параллелизма и будут масштабироваться естественным путем, но многоядерные особенно актуальны для настольных приложений, которые традиционно делают только несколько вещей одновременно - часто только один Задача переднего плана с фоновым потоком. Теперь они должны быть закодированы для одновременного выполнения многих задач, если они хотят воспользоваться мощью многоядерного процессора.

Что касается производительности - на одноядерных - асинхронные задачи замедляют работу системы так же, как если бы они выполнялись последовательно (это упрощение, но в большинстве случаев верно). Итак, выполнение задачи A, которая занимает 10 с и для задачи B, которая занимает 5 секунд на одном ядре, общее необходимое время составит 15 секунд, если B выполняется асинхронно или нет. Причина в том, что при запуске B он отнимает ресурсы процессора у A - A, а B конкурирует за один процессор.

На многоядерном компьютере дополнительные задачи выполняются на неиспользуемых ядрах, в противном случае ситуация иная - дополнительные задачи на самом деле не занимают время - или, вернее, они не отнимают время у ядра выполнение задачи A. Таким образом, выполнение задач A и B в асинхронном режиме на многоядерных процессорах будет занимать всего 10 секунд, а не 15 секунд, как с одним ядром. Выполнение B выполняется одновременно с A и на отдельном ядре, поэтому время выполнения A не изменяется.

По мере увеличения числа задач и ядер потенциальные улучшения производительности также увеличиваются. В параллельных вычислениях использование параллелизма для повышения производительности известно как speedup .

мы уже видим 64-ядерный процессор, и предполагается, что через 10 лет у нас будет 1024 ядра. Это потенциальное ускорение в 1024 раза по сравнению с однопоточным синхронным случаем. Итак, чтобы ответить на ваш вопрос, очевидно, что при использовании асинхронного выполнения вы получите выигрыш в производительности.

0 голосов
/ 21 октября 2015

Я полагал, что любой вид асинхронного выполнения создает поток в невидимой области.

Это ваша проблема - на самом деле это не так.

Дело в том,весь ваш компьютер фактически асинхронен - ​​запросы к оперативной памяти, связь через сетевую карту, доступ к жесткому диску ... все это по своей сути асинхронные операции.

Современные ОС фактически построены на асинхронном вводе-выводе.Например, даже если вы выполняете синхронный запрос файла (например, File.ReadAllText), ОС отправляет асинхронный запрос.Однако вместо того, чтобы вернуть управление вашему коду, он блокирует , ожидая ответа на асинхронный запрос.И именно здесь приходит правильный асинхронный код - вместо ожидания ответа вы предоставляете запросу callback - функцию, которая будет выполняться при возврате ответа.

На времяв асинхронном запросе нет потока .Все это происходит на совершенно другом уровне - скажем, запрос отправляется в прошивку на вашей сетевой карте и ему дается адрес DMA для заполнения ответа.Когда NIC завершает ваш запрос, он заполняет память и сигнализирует прерывание процессору.Ядро ОС обрабатывает прерывание, сигнализируя приложению-владельцу (обычно «канал» IOCP), что запрос выполнен.Все это по-прежнему выполняется без какого-либо потока - только на короткое время в конце поток заимствуется (в .NET это из пула потоков IOCP) для выполнения обратного вызова.

Итак, представьте себепростой сценарий.Вам нужно отправить 100 одновременных запросов к базе данных.С многопоточностью вы бы запускали новую ветку для каждого из этих запросов.Это означает сотню потоков, сотни потоков, стоимость запуска самого нового потока (запуск нового потока стоит дешево - запуск сотен одновременно, не так много), совсем немного ресурсов.И эти темы просто ... заблокируют.Ничего не делать.Когда приходит ответ, потоки просыпаются один за другим и в конечном итоге удаляются.

С другой стороны, с помощью асинхронного ввода-вывода вы можете просто отправить все запросы из одного потока - и зарегистрироватьобратный вызов, когда каждый из них закончен.Сотня одновременных запросов будет стоить вам только вашего исходного потока (который будет свободен для другой работы, как только запросы будут опубликованы), и короткое время с потоками из пула потоков, когда запросы будут завершены - в «худшем» случае,примерно столько потоков, сколько у вас процессорных ядер.Конечно, при условии, что вы не используете код блокировки в обратном вызове:)

Это не обязательно означает, что асинхронный код автоматически становится более эффективным.Если вам нужен только один запрос, и вы ничего не можете сделать, пока не получите ответ, бессмысленно делать запрос асинхронным.Но в большинстве случаев это не ваш реальный сценарий - например, вам нужно поддерживать графический интерфейс в то же время, или вам нужно делать одновременные запросы, или весь ваш код основан на обратном вызове, а не записывается синхронно (типичныйПриложение .NET Windows Forms в основном основано на событиях).

Реальная выгода от асинхронного кода заключается именно в этом: упрощенный неблокирующий код пользовательского интерфейса (не более предупреждений "(не отвечает)" из оконного менеджера)и значительно улучшенный параллелизм.Если у вас есть веб-сервер, который обрабатывает тысячу запросов одновременно, вы не хотите тратить 1 ГБ адресного пространства только на совершенно ненужные стеки потоков (особенно в 32-битной системе) - вы используете потоки только тогда, когда у вас есть что-тоделать.

Итак, в конце концов, асинхронный код значительно упрощает пользовательский интерфейс и код сервера.В некоторых случаях, в основном с серверами, это также может сделать его намного более эффективным.Повышение эффективности происходит именно благодаря тому, что отсутствует поток во время выполнения асинхронного запроса.

Ваш комментарий относится только к одному конкретному виду асинхронного кода - многопоточному параллелизму.В этом случае вы действительно теряете поток при выполнении запроса.Однако это не то, что люди имеют в виду, когда говорят, что «моя библиотека предлагает асинхронный API» - в конце концов, это 100% бесполезный API;Вы могли бы просто позвонить await Task.Run(TheirAPIMethod) и получить то же самое.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...