Ответ 1
ОбъектA Task
представляет отложенный результат ожидающей операции. Вам не нужно использовать задачи и async/await
, если у вас нет ожидающих операций. В противном случае, я считаю, что код async
/await
обычно более эффективен, чем его голой аналог TPL ContinueWith
.
Давайте сделаем некоторое время:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication
{
class Program
{
// async/await version
static async Task<int> Test1Async(Task<int> task)
{
return await task;
}
// TPL version
static Task<int> Test2Async(Task<int> task)
{
return task.ContinueWith(
t => t.Result,
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
}
static void Tester(string name, Func<Task<int>, Task<int>> func)
{
var sw = new System.Diagnostics.Stopwatch();
sw.Start();
for (int i = 0; i < 10000000; i++)
{
func(Task.FromResult(0)).Wait();
}
sw.Stop();
Console.WriteLine("{0}: {1}ms", name, sw.ElapsedMilliseconds);
}
static void Main(string[] args)
{
Tester("Test1Async", Test1Async);
Tester("Test2Async", Test2Async);
}
}
}
Выход:
Test1Async: 1582ms Test2Async: 4975ms
Итак, по умолчанию, await
продолжения обрабатываются более эффективно, чем продолжения ContinueWith
. Немного оптимизируйте этот код:
// async/await version
static async Task<int> Test1Async(Task<int> task)
{
if (task.IsCompleted)
return task.Result;
return await task;
}
// TPL version
static Task<int> Test2Async(Task<int> task)
{
if (task.IsCompleted)
return Task.FromResult(task.Result);
return task.ContinueWith(
t => t.Result,
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
}
Выход:
Test1Async: 1557ms Test2Async: 429ms
Теперь выигрывает неасинхронная версия. В случае с версией async
, я считаю, что эта оптимизация уже была внутренне реализована инфраструктурой async/await
.
В любом случае, до сих пор мы занимались только выполненными задачами (Task.FromResult
). Введем фактическую асинхронность (естественно, на этот раз мы будем делать меньше итераций):
static Task<int> DoAsync()
{
var tcs = new TaskCompletionSource<int>();
ThreadPool.QueueUserWorkItem(_ => tcs.SetResult(0));
return tcs.Task;
}
static void Tester(string name, Func<Task<int>, Task<int>> func)
{
ThreadPool.SetMinThreads(200, 200);
var sw = new System.Diagnostics.Stopwatch();
sw.Start();
for (int i = 0; i < 1000000; i++)
{
func(DoAsync()).Wait();
}
sw.Stop();
Console.WriteLine("{0}: {1}ms", name, sw.ElapsedMilliseconds);
}
Выход:
Test1Async: 4207ms Test2Async: 4734ms
Теперь разница очень незначительна, хотя версия async
все еще работает немного лучше. Тем не менее, я думаю, что такой выигрыш действительно пренебрежимо мал, сравнимый с фактической стоимостью асинхронной операции или с затратами на восстановление захваченного контекста, когда SynchronizationContext.Current != null
.
Суть в том, что если вы занимаетесь асинхронными задачами, перейдите к async
/await
, если у вас есть выбор, а не по соображениям производительности, но для удобства использования, удобочитаемости и ремонтопригодности.