Никогда не используйте конструктор Task
. Это удваивается на ASP. NET, поскольку построенные задачи всегда являются делегированными задачами, которые мешают использованию ASP. NET пула потоков . Фактическая причина, по которой await
зависает, заключается в том, что задачи, созданные вручную, должны быть запущены .
Если у вас есть синхронная работа, которую нужно заключить в Task
, чтобы работать вместе асинхронных задач, тогда вы должны использовать Task.CompletedTask
и Task.FromException
:
private static Task SynchronousWork(Func<IEnumerable<IExecutionResult>, TOutput> outputBuilder)
{
try { _output = outputBuilder(_results); return Task.CompletedTask; }
catch (Exception ex) { return Task.FromException(ex); }
}
public IPipeBuilder<TOutput> Returns(Func<IEnumerable<IExecutionResult>, TOutput> outputBuilder)
{
_pipe.Enqueue(SynchronousWork(outputBuilder));
return this;
}
Однако обратите внимание, что это выполняет outputBuilder
немедленно , что может быть нежелательно из-за его стороны эффекты на _results
и _output
. Если вы хотите очередь отложенного выполнения , то тип в очереди должен быть изменен с Task
на Func<Task>
. Затем вы можете добавить к нему следующее:
public IPipeBuilder<TOutput> Returns(Func<IEnumerable<IExecutionResult>, TOutput> outputBuilder)
{
_pipe.Enqueue(() =>
{
try { _output = outputBuilder(_results); return Task.CompletedTask; }
catch (Exception ex) { return Task.FromException(ex); }
});
return this;
}
и использовать его, вызывая каждого делегата по одному и проверяя возвращаемую им задачу:
public async Task<TOutput> Execute()
{
while (_pipe.TryDequeue(out var currentFunc))
{
var currentTask = currentFunc();
if (currentTask.IsCommandExecution())
{
IExecutionResult result = await (Task<IExecutionResult>)currentTask;
_results.Add(result);
}
else
{
await currentTask;
}
}
return _output;
}