Рассмотрим следующее (на основе шаблона MVC по умолчанию), который представляет собой упрощенную версию некоторого "материала", который происходит в фоновом режиме - он отлично завершен и показывает ожидаемый результат, 20:
public ActionResult Index()
{
var task = SlowDouble(10);
string result;
if (task.Wait(2000))
{
result = task.Result.ToString();
}
else
{
result = "timeout";
}
ViewBag.Message = result;
return View();
}
internal static Task<long> SlowDouble(long val)
{
TaskCompletionSource<long> result = new TaskCompletionSource<long>();
ThreadPool.QueueUserWorkItem(delegate
{
Thread.Sleep(50);
result.SetResult(val * 2);
});
return result.Task;
}
Однако теперь, если мы добавим в микс async
:
public static async Task<long> IndirectSlowDouble(long val)
{
long result = await SlowDouble(val);
return result;
}
и измените первую строку маршрута на:
var task = IndirectSlowDouble(10);
тогда не работает; время от времени. Если мы добавим точки останова, return result;
в методе async
произойдет только после, маршрут уже завершен - в основном, похоже, что система не хочет использовать какой-либо поток для возобновления async
до тех пор, пока запрос не будет завершен. Хуже того: если бы мы использовали .Wait()
(или получили доступ к .Result
), тогда он будет полностью заторможен.
Итак: что с этим? Очевидным обходным решением является "не включать async
", но это нелегко при использовании библиотек и т.д. В конечном счете нет функциональной разницы между SlowDouble
и IndirectSlowDouble
(хотя очевидная структурная разница).
Примечание: точно такая же вещь в консоли /winform/etc будет работать нормально.