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

Task.Factory.StartNew vs. Task.Factory.FromAsync

Предположим, что у нас есть связанный с I/O метод (например, метод, вызывающий вызовы БД). Этот метод можно запускать как синхронно, так и асинхронно. То есть

  • Синхронизация:

    IOMethod()
    
  • Асинхронный:

    BeginIOMethod()
    EndIOMethod()
    

Затем, когда мы выполняем метод по-разному, как показано ниже, какова разница в производительности с точки зрения использования ресурсов?

  • var task = Task.Factory.StartNew(() => { IOMethod(); });
    task.Wait();
    
  • var task = Task.Factory.FromAsync(BeginIOMethod, EndIOMethod, ... );
    task.Wait();
    
4b9b3361

Ответ 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.)