我想我不明白什么。我原以为 Task.Yield()
会强制为任务启动一个新的线程/上下文,但是在 re-reading this answer 上似乎它只是强制该方法是异步的。它仍将处于相同的上下文中。
在 asp.net 进程中,并行创建和运行多个任务而不导致死锁的正确方法是什么?
换句话说,假设我有以下方法:
async Task createFileFromLongRunningComputation(int input) {
//many levels of async code
}
当某个 POST 路由被命中时,我想同时启动上述方法 3 次,立即返回,但在所有三个都完成后记录。
我想我需要把这样的东西放到我的 Action 中
public IHttpAction Post() {
Task.WhenAll(
createFileFromLongRunningComputation(1),
createFileFromLongRunningComputation(2),
createFileFromLongRunningComputation(3)
).ContinueWith((Task t) =>
logger.Log("Computation completed")
).ConfigureAwait(false);
return Ok();
createFileFromLongRunningComputation
需要做什么?我原以为 Task.Yield
是正确的,但显然不是。
最佳答案
将并发工作卸载到不同线程的正确方法是按照 rossipedia 的建议使用 Task.Run
。
ASP.Net 中后台处理的最佳解决方案(您的 AppDomain
可以与您的所有任务一起自动回收/关闭)在 Scott Hanselman 中。和 Stephen Cleary的博客(例如 HangFire)
但是,您可以将Task.Yield
与ConfigureAwait(false)
一起使用来实现相同的目的。
所有 Task.Yield
所做的就是返回一个等待程序,以确保方法的其余部分不会同步进行(通过让 IsCompleted
返回 false
和 OnCompleted
立即执行 Action
参数)。 ConfigureAwait(false)
忽略 SynchronizationContext
,因此强制该方法的其余部分在 ThreadPool
线程上执行。
如果同时使用两者,您可以确保 async
方法立即返回一个任务,该任务将在 ThreadPool
线程上执行(如 Task.Run
):
async Task CreateFileFromLongRunningComputation(int input)
{
await Task.Yield().ConfigureAwait(false);
// executed on a ThreadPool thread
}
编辑:
George Mauer 指出,由于 Task.Yield
返回 YieldAwaitable
,您不能使用 ConfigureAwait(false)
这是 上的一个方法任务
类。
您可以通过使用Task.Delay
实现类似的事情,超时时间很短,因此它不会是同步的,但您不会浪费太多时间:
async Task CreateFileFromLongRunningComputation(int input)
{
await Task.Delay(1).ConfigureAwait(false);
// executed on a ThreadPool thread
}
更好的选择是创建一个简单地忽略 SynchronizationContext
的 YieldAwaitable
,就像使用 ConfigureAwait(false)
一样:
async Task CreateFileFromLongRunningComputation(int input)
{
await new NoContextYieldAwaitable();
// executed on a ThreadPool thread
}
public struct NoContextYieldAwaitable
{
public NoContextYieldAwaiter GetAwaiter() { return new NoContextYieldAwaiter(); }
public struct NoContextYieldAwaiter : INotifyCompletion
{
public bool IsCompleted { get { return false; } }
public void OnCompleted(Action continuation)
{
var scheduler = TaskScheduler.Current;
if (scheduler == TaskScheduler.Default)
{
ThreadPool.QueueUserWorkItem(RunAction, continuation);
}
else
{
Task.Factory.StartNew(continuation, CancellationToken.None, TaskCreationOptions.PreferFairness, scheduler);
}
}
public void GetResult() { }
private static void RunAction(object state) { ((Action)state)(); }
}
}
这不是建议,而是对您的 Task.Yield 问题的回答。
关于c# - 在 asp.net 进程中运行多个并行任务的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27987713/