Подтвердить что ты не робот

Разница между TPL и async/await (Обработка потоков)

Попытка понять разницу между TPL и async/await, когда дело доходит до создания потоков.

Я считаю, что TPL (TaskFactory.Startnew) работает подобно ThreadPool.QueueUserWorkItem тем, что он ставит в очередь работу над потоком в пуле потоков. Конечно, если вы не используете TaskCreationOptions.LongRunning, которые создают новый поток.

Я думал, что async/await будет работать так же существенно:

TPL:

Factory.StartNew( () => DoSomeAsyncWork() )
.ContinueWith( 
    (antecedent) => {
        DoSomeWorkAfter(); 
    },TaskScheduler.FromCurrentSynchronizationContext());

Асинхронный/Await:

await DoSomeAsyncWork();  
DoSomeWorkAfter();

будет идентичным. Из того, что я читал, кажется, что async/await только "иногда" создает новый поток. Итак, когда он создает новый поток и когда он не создает новый поток? Если вы имели дело с портами ввода-вывода IO, я вижу, что вам не нужно создавать новый поток, но в противном случае я бы подумал, что это нужно. Я думаю, что мое понимание FromCurrentSynchronizationContext всегда было немного нечетким. Я всегда понимал, что это был, по сути, поток пользовательского интерфейса.

4b9b3361

Ответ 1

Я считаю, что TPL (TaskFactory.Startnew) работает аналогично ThreadPool.QueueUserWorkItem в том, что он приостанавливает работу над потоком в пуле потоков.

Довольно много.

Из того, что я читал, кажется, что async/await только "иногда" создает новый поток.

Собственно, этого никогда не происходит. Если вы хотите многопоточность, вы должны реализовать ее самостоятельно. Там есть новый метод Task.Run, который просто сокращен для Task.Factory.StartNew, и это, вероятно, самый распространенный способ запуска задачи в пуле потоков.

Если вы имели дело с портами ввода-вывода IO, я вижу, что вам не нужно создавать новый поток, но в противном случае я бы подумал, что это будет нужно.

Бинго. Таким образом, методы, подобные Stream.ReadAsync, фактически создадут обертку Task вокруг IOCP (если Stream имеет IOCP).

Вы также можете создавать некоторые задачи, отличные от ввода-вывода, не-ЦП. Простым примером является Task.Delay, который возвращает задание, которое завершается через некоторый период времени.

Приятная вещь о async/await заключается в том, что вы можете заказать некоторую работу в пуле потоков (например, Task.Run), выполнить некоторую операцию с привязкой к I/O (например, Stream.ReadAsync) и выполните некоторую другую операцию (например, Task.Delay)... и это все задачи! Их можно ожидать или использовать в комбинациях, таких как Task.WhenAll.

Любой метод, возвращающий Task, может быть await ed - он не должен быть async. Таким образом, операции Task.Delay и I/O-привязки просто используют TaskCompletionSource для создания и завершения задачи - единственное, что делается в пуле потоков, - это фактическое завершение задачи при возникновении события (тайм-аут, завершение ввода-вывода и т.д.).).

Я думаю, что мое понимание FromCurrentSynchronizationContext всегда было немного нечетким. Я всегда понимал, что это был, по сути, поток пользовательского интерфейса.

Я написал статью в SynchronizationContext. Большую часть времени SynchronizationContext.Current:

  • является контекстом пользовательского интерфейса, если текущий поток является потоком пользовательского интерфейса.
  • - это контекст запроса ASP.NET, если текущий поток обслуживает запрос ASP.NET.
  • - это контекст пула потоков в противном случае.

Любой поток может установить свой собственный SynchronizationContext, поэтому есть исключения из правил выше.

Обратите внимание, что awaiter Task по умолчанию будет планировать оставшуюся часть метода async для текущего SynchronizationContext, если он не равен null; в противном случае он переходит в текущий TaskScheduler. Сегодня это не так важно, но в ближайшем будущем это будет важное различие.

Я написал свой собственный async/await intro в своем блоге, и Стивен Туб недавно опубликовал отличный async/await Часто задаваемые вопросы.

Относительно "concurrency" и "многопоточности" см. этот связанный вопрос SO. Я бы сказал, что async включает concurrency, который может быть или не быть многопоточным. Легко использовать await Task.WhenAll или await Task.WhenAny для параллельной обработки, и если вы явно не используете пул потоков (например, Task.Run или ConfigureAwait(false)), то вы можете одновременно выполнять несколько одновременных операций ( например, несколько операций ввода-вывода или других типов, таких как Delay), и нет необходимости в потоке. Я использую термин "однопоточный concurrency" для такого рода сценариев, хотя на хосте ASP.NET вы можете на самом деле "с нулевой резьбой concurrency" . Это довольно мило.

Ответ 2

async/await в основном упрощает методы ContinueWith (Продолжения в Стиль перехода к продолжению)

В него не вводится concurrency - вам все равно нужно сделать это самостоятельно (или использовать Async-версию метода framework.)

Итак, версия С# 5 будет:

await Task.Run( () => DoSomeAsyncWork() );
DoSomeWorkAfter();