c# - 在 asp.net 进程中运行多个并行任务的正确方法是什么?

标签 c# asp.net multithreading task-parallel-library async-await

我想我不明白什么。我原以为 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.YieldConfigureAwait(false) 一起使用来实现相同的目的。

所有 Task.Yield 所做的就是返回一个等待程序,以确保方法的其余部分不会同步进行(通过让 IsCompleted 返回 falseOnCompleted 立即执行 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
}

更好的选择是创建一个简单地忽略 SynchronizationContextYieldAwaitable,就像使用 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/

相关文章:

c# - 如何使用 LINQ 选择然后 OrderBy?

c# - 在运行内部代码之前,在锁之前和锁内设置 "double check"是否可以?

c++ - 为什么 OpenMP 不允许使用 != 运算符?

c# - Resharper:如何查找接口(interface)特定实现的用法

c# - 从 C# 调用非托管 (C++) 函数时出现 PInvoke 错误

c# - 如何找到元素在 Web 浏览器控件中的位置?

java - Java 5 中的 Lock 和 ReentrantLock 有什么区别?

c# - 查询反序列化对象

asp.net - 配置 IIS 为目录浏览尝试返回 404

asp.net - 文本框 TextMode ="Email"asp.net