Ответ 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" . Это довольно мило.