【问题标题】:Task.Factory.StartNew with async lambda and Task.WaitAll带有异步 lambda 和 Task.WaitAll 的 Task.Factory.StartNew
【发布时间】:2015-08-29 10:53:48
【问题描述】:

我正在尝试在任务列表中使用Task.WaitAll。问题是任务是一个异步 lambda,它会破坏 Tasks.WaitAll,因为它从不等待。

这是一个示例代码块:

List<Task> tasks = new List<Task>();
tasks.Add(Task.Factory.StartNew(async () =>
{
    using (dbContext = new DatabaseContext())
    {
        var records = await dbContext.Where(r => r.Id = 100).ToListAsync();
        //do long cpu process here...
    }
}
Task.WaitAll(tasks);
//do more stuff here  

由于异步 lambda,这不会等待。那么我应该如何在我的 lambda 中等待 I/O 操作呢?

【问题讨论】:

  • 如果您在启动任务后做的第一件事是阻止 Task.WaitAll 调用,那么在另一个线程上启动任务有什么意义?您将获得更好的性能摆脱ToListAsync 并使其成为ToList 并同步运行它。 (或者如果您确实想使用ToListAsync,那么您需要在调用堆栈中一直使用异步。

标签: c# .net lambda async-await task-parallel-library


【解决方案1】:

Task.Factory.StartNew 无法识别async 委托,因为没有接受返回Task 的函数的重载。

这加上其他原因(请参阅StartNew is dangerous)是您应该在此处使用Task.Run 的原因:

tasks.Add(Task.Run(async () => ...

【讨论】:

  • 这工作......有点。现在不确定问题,但我收到一个任务取消异常,试图返回一个字符串。似乎是一个不同的问题,所以我要开始一个新的 SO 线程。谢谢。
  • 太糟糕了 Task.run 没有给你TaskCreationOptions
  • Task.Factory.StartNew(Func&lt;Task&gt; function 似乎在 .NET Standard 1.6 中可用 - 可能必须 Unwrap() 得到 Task 虽然 :)
【解决方案2】:

由于异步 lambda,这不会等待。那我该怎么做 在我的 lambda 中等待 I/O 操作?

Task.WaitAll 不等待异步 lambda 提供的 IO 工作完成的原因是,Task.Factory.StartNew 实际上返回了 Task&lt;Task&gt;。由于您的列表是 List&lt;Task&gt;(并且 Task&lt;T&gt; 派生自 Task),因此您等待由 StartNew 启动的外部任务,同时忽略由异步 lambda 创建的内部任务。这就是为什么他们说Task.Factory.StartNew 在异步方面是危险的。

你怎么能解决这个问题?您可以显式调用Task&lt;Task&gt;.Unwrap() 以获取内部任务:

List<Task> tasks = new List<Task>();
tasks.Add(Task.Factory.StartNew(async () =>
{
    using (dbContext = new DatabaseContext())
    {
        var records = await dbContext.Where(r => r.Id = 100).ToListAsync();
        //do long cpu process here...
    }
}).Unwrap());

或者像其他人说的,你可以打电话给Task.Run

tasks.Add(Task.Run(async () => /* lambda */);

另外,既然你想把事情做对,你会想使用Task.WhenAll,为什么异步等待,而不是Task.WaitAll同步阻塞:

await Task.WhenAll(tasks);

【讨论】:

    【解决方案3】:

    你可以这样做。

        void Something()
        {
            List<Task> tasks = new List<Task>();
            tasks.Add(ReadAsync());
            Task.WaitAll(tasks.ToArray());
        }
    
        async Task ReadAsync() {
            using (dbContext = new DatabaseContext())
            {
                var records = await dbContext.Where(r => r.Id = 100).ToListAsync();
                //do long cpu process here...
            }
        }
    

    【讨论】:

    • 这几乎是一个很好的答案。如果您使用的是async,则不应阻止Task.WaitAll,因为您很容易死锁。
    • WaitAll 是 Jacob 根据问题想要做的事情。
    【解决方案4】:

    您必须使用Task.ContinueWith 方法。像这样

    List<Task> tasks = new List<Task>();
    tasks.Add(Task.Factory.StartNew(() =>
    {
        using (dbContext = new DatabaseContext())
        {
            return dbContext.Where(r => r.Id = 100).ToListAsync().ContinueWith(t =>
                {
                    var records = t.Result;
                    // do long cpu process here...
                });
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-11-30
      • 2017-03-13
      • 2019-06-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多