带有异步 lambda 和 Task.WaitAll 的 Task.Factory.StartNew
Posted
技术标签:
【中文标题】带有异步 lambda 和 Task.WaitAll 的 Task.Factory.StartNew【英文标题】:Task.Factory.StartNew with async lambda and Task.WaitAll 【发布时间】: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
,那么您需要在调用堆栈中一直使用异步。
【参考方案1】:
Task.Factory.StartNew
无法识别async
委托,因为没有接受返回Task
的函数的重载。
这加上其他原因(请参阅StartNew is dangerous)是您应该在此处使用Task.Run
的原因:
tasks.Add(Task.Run(async () => ...
【讨论】:
这工作......有点。现在不确定问题,但我收到一个任务取消异常,试图返回一个字符串。似乎是一个不同的问题,所以我要开始一个新的 SO 线程。谢谢。 太糟糕了 Task.run 没有给你TaskCreationOptions
Task.Factory.StartNew(Func<Task> function
似乎在 .NET Standard 1.6 中可用 - 可能必须 Unwrap()
得到 Task
虽然 :)【参考方案2】:
由于异步 lambda,这不会等待。那我该怎么做 在我的 lambda 中等待 I/O 操作?
Task.WaitAll
不等待异步 lambda 提供的 IO 工作完成的原因是,Task.Factory.StartNew
实际上返回了 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);
【讨论】:
【参考方案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...
);
【讨论】:
以上是关于带有异步 lambda 和 Task.WaitAll 的 Task.Factory.StartNew的主要内容,如果未能解决你的问题,请参考以下文章
在异步 AWS Lambda 函数中使用带有 node-fetch 模块的 node.js 时遇到问题