Task.Factory.StartNew 使用异步 lambda 和 Task.WaitAll

Task.Factory.StartNew with async lambda and Task.WaitAll

我正尝试在任务列表中使用 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.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...
            });
        }
    }
}

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

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

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

你可以这样做。

    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...
        }
    }

This doesn't wait because of the async lambda. So how am I supposed to await I/O operations in my lambda?

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

你怎么解决这个问题的?您可以显式调用 Task<Task>.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);