我做了一个队列,其中包含要执行的任务。用new Task()
方法中的Returns
手动创建一些任务后,我的整个应用程序挂起-await current;
。任务的主体甚至没有触发。
ConfigureAwait(false)
没有帮助。
队列中的第一个任务不是我创建的,而是其他框架正在成功执行并返回值。我的-不。我尝试添加Task.CompletedTask
,然后它起作用了。我不明白为什么我什至无法达到包含_output
分配任务的内容。
--- 更新 ---
当我使用以下代码时,该代码有效。使用await
不会。有什么想法吗?
current.Start();
current.Wait();
原始代码
private readonly Queue<Task> _pipe;
public IPipeBuilder<TOutput> Returns(Func<IEnumerable<IExecutionResult>, TOutput> outputBuilder)
{
_pipe.Enqueue(new Task(() => // this task causes a problem and breakpoint isn't hit inside
{
_output = outputBuilder(_results);
}));
return this;
}
public async Task<TOutput> Execute()
{
Task current;
while (_pipe.TryDequeue(out current))
{
if (current.IsCommandExecution())
{
IExecutionResult result = await (Task<IExecutionResult>)current; // this awaits successfully
_results.Add(result);
}
else
{
await current; // hangs here
}
}
return await Task.FromResult(_output);
}
用法
[HttpGet("eventflow/pipe/issue/add/{title}")]
public async Task<IActionResult> PipeAction(string title)
=> Ok(
await Pipe<IExecutionResult>()
.Validate(title)
.Handle<AddIssueCommand>(IssueId.New, title)
.Returns(results => results.First())
.Execute());
答案 0 :(得分:2)
You should never use the Task
constructor。在ASP.NET上,它具有两倍的效果,因为构造的任务始终是委托任务,interfere with the ASP.NET usage of the thread pool。 await
挂起的实际原因是因为manually-created tasks need to be started。
如果您需要将同步工作包装到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;
}
答案 1 :(得分:0)
好的,谢谢。我结束了这样的课程,并且像您说的那样Queue<Func<Task>>
。
public sealed class SyncTaskWrapper
{
private Func<Task> _action;
public SyncTaskWrapper(Action action)
=> _action = CreateFunc(action);
private static Func<Task> CreateFunc(Action action)
=> () =>
{
try
{
action();
return Task.CompletedTask;
}
catch (Exception exception)
{
return Task.FromException(exception);
}
};
public static implicit operator Func<Task>(SyncTaskWrapper @this)
=> @this._action;
}
随用法
_pipe.Enqueue(new SyncTaskWrapper(() =>
_output = outputBuilder(_results)));