Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
Вызывать Unwrap после StartNew нужно лишь в случае когда передаётся асинхронный делегат в качестве параметра (как в вашем примере), потому что такой делегат вернёт Task, который завернётся в ещё один Task, и его нужно развернуть. В прочих случаях никакой Unwrap не нужен.
public static Task<TResult> Run<TResult>(Func<Task<TResult>> function)
public Task<TResult> StartNew<TResult>(Func<TResult> function);В прочих случаях никакой Unwrap не нужен
+
Unwrap нужен с любым типом результата
+
Вместо Unwrap можно использовать двойной await (return await await ...)
Всегда используйте метод-расширение Unwrap для Task.Factory.StartNew(). Это сделает ваш код более идиоматичным (один await на вызов) и не допустит хитростей dynamic.
Чтобы стало нагляднее, рассмотрим пример:
static Task<T> Compute<T>(Task<T> inner) { return Task.Run(async () => await inner); }
static async Task<T> ComputeWithFactory<T>(Task<T> inner) { return await await Task.Factory.StartNew(async () => await inner); }
Да, есть проблема, но, как и у g_DiGGeR, проблема в понимании: зачем в Compute передавать Task; a если уж приспичило передавать именно Task, то зачем его там стартовать вместо того, чтобы сразу отдать (безо всякого return await); а если вдруг нужно дождаться результата inner и использовать внутри Compute, то почему не использовать await inner напрямую, без StartNew с асинхронной блямбдой и т.п.?
That could be perfectly reasonable if the taskFactory delegate does very little work before returning the task instance. If, however, the taskFactory delegate does any non-negligable work, a call to Value would block until the call to taskFactory completes. To cover that case, the second approach is to run the taskFactory using Task.Factory.StartNew, i.e. to run the delegate itself asynchronously, just as with the first constructor, even though this delegate already returns a Task. Of course, now StartNew will be returning a Task<Task>, so we use the Unwrap method in .NET 4 to convert the Task<Task> into a Task, and that’s what’s passed down the base type.
Не совсем понял, внутри чего окажется таск? И я что-то в три часа ночи уже не соображу, как совместить перегрузку по Action (который и сам синхронный и не возвращает ничего асинхронного) с асинхронной лямбдой? Ну если только не ставить async-и и лямбды бездумно куда ни попадя.
static void ComputeNoReturn(Action inner)
{
inner();
}
ComputeNoReturn(async () =>
{
throw new Exception();
});
ComputeNoReturn(async () =>
{
await Task.Run(() => {});
});Удивляет, зачем использовать динамик (сознательно) вместо нормальной параметризации и жаловаться на это? Как только типы проставлены, компилятор заставит подумать.
Решение использвать Unwrap это не решение, а просто корректный способ использования TPL.
Правила работы с Tasks API. Часть 1