Task.Factory.StartNew vs. Task.Factory.FromAsync
Предположим, что у нас есть связанный с I/O метод (например, метод, вызывающий вызовы БД). Этот метод можно запускать как синхронно, так и асинхронно. То есть
Затем, когда мы выполняем метод по-разному, как показано ниже, какова разница в производительности с точки зрения использования ресурсов?
-
var task = Task.Factory.StartNew(() => { IOMethod(); });
task.Wait();
-
var task = Task.Factory.FromAsync(BeginIOMethod, EndIOMethod, ... );
task.Wait();
Ответы
Ответ 1
var task = Task.Factory.StartNew(() => { IOMethod(); });
task.Wait();
Это блокирует поток пула потоков, пока выполняется IOMethod()
, а также блокирует текущий поток из-за Wait()
. Всего заблокированных потоков: 2.
var task = Task.Factory.FromAsync(BeginIOMethod, EndIOMethod, ... );
task.Wait();
Это, скорее всего, выполнит операцию асинхронно без использования потока, но будет блокировать текущий поток из-за Wait()
. Всего заблокированных потоков: 1.
IOMethod();
Это заблокирует текущий поток, пока выполняется IOMethod()
. Всего заблокированных потоков: 1.
Если вам нужно заблокировать текущий поток или блокировать его для вас, то вы должны использовать это, потому что попытка использовать TPL на самом деле ничего не даст.
var task = Task.Factory.FromAsync(BeginIOMethod, EndIOMethod, ... );
await task;
Это приведет к асинхронной работе без использования потока, и он также ожидает, что операция завершится асинхронно, благодаря await
. Всего заблокированных потоков: 0.
Это то, что вы должны использовать, если хотите использовать асинхронность, и вы можете использовать С# 5.0.
var task = Task.Factory.FromAsync(BeginIOMethod, EndIOMethod, ... );
task.ContinueWith(() => /* rest of the method here */);
Это приведет к асинхронной работе без использования потока, и он также ожидает, что операция завершится асинхронно, благодаря ContinueWith()
. Всего заблокированных потоков: 0.
Это то, что вы должны использовать, если хотите использовать асинхронность, и вы не можете использовать С# 5.0.
Ответ 2
(1) приведет (вероятно) к тому, что пул потоков .NET обработает ваш Task
.
(2) будет использовать любой механизм, который использует ваша пара BeginIOMethod
/EndIOMethod
для обработки асинхронной части, которая может включать или не включать пул потоков .NET.
Например, если ваш BeginIOMethod
отправляет TCP-сообщение через Интернет, а позднее получатель отправит вам ответ TCP (полученный EndIOMethod
), тогда асинхронный характер пул потоков .NET не поддерживается. Используемая библиотека TCP предоставляет асинхронную часть.
Это можно сделать, используя класс TaskCompletionSource
. Task.Factory.FromAsync
может создать TaskCompletionSource<T>
, вернуть его Task<T>
, а затем использовать EndIOMethod
в качестве триггера для размещения Result
в Task<T>
, который был возвращен формой Task.Factory.FromAsync
во время вызова.
Какая разница в производительности с точки зрения использования ресурсов?
Разница между (1) и (2) заключается, прежде всего, в том, будет ли добавлен поток рабочей нагрузки .NET или нет. В общем, правильная вещь - выбрать Task.Factory.FromAsync
, если у вас есть только пара Begin...
/End...
и Task.Factory.StartNew
в противном случае.
Если вы используете С# 5.0, вы должны использовать неблокирующий await task;
вместо task.Wait();
. (См. Ответ svick.)