【发布时间】:2018-09-14 06:16:38
【问题描述】:
我正在尝试使用 asp.net core 2.1 实现自托管 Web 服务,但遇到了实现后台长时间执行任务的问题。
由于每个ProcessSingle方法(在下面的代码sn-p中)的高CPU负载和时间消耗,我想限制同时执行的任务的数量。但是我几乎可以立即看到Parallel.ForEachstart 中的所有任务,尽管我设置了MaxDegreeOfParallelism = 3
我的代码是(简化版):
public static async Task<int> Work()
{
var id = await CreateIdInDB() // async create record in DB
// run background task, don't wait when it finishes
Task.Factory.StartNew(async () => {
Parallel.ForEach(
listOfData,
new ParallelOptions { CancellationToken = token, MaxDegreeOfParallelism = 3 },
async x => await ProcessSingle(x));
});
// return created id immediately
return id;
}
public static async Task ProcessSingle(MyInputData inputData)
{
var dbData = await GetDataFromDb(); // get data from DB async using Dapper
// some lasting processing (sync)
await SaveDataToDb(); // async save processed data to DB using Dapper
}
如果我理解正确,问题出在 Parallel.ForEach 内的async x => await ProcessSingle(x),不是吗?
有人可以描述一下,它应该如何以正确的方式实施?
更新
由于我的问题有些模棱两可,有必要关注主要方面:
-
ProcessSingle方法分三部分:从数据库异步获取数据
进行长时间高 CPU 负载的数学计算
将结果保存到数据库异步
-
这个问题包括两个独立的:
如何降低 CPU 使用率(例如同时运行不超过三个数学计算)?
如何保持
ProcessSingle方法的结构 - 因为异步 DB 调用而使它们保持异步。
希望现在会更清楚。
附:已经给出了合适的答案,它可以工作(特别感谢@MatrixTai)。编写此更新是为了进行一般说明。
【问题讨论】:
-
您的 foreach 循环中不需要 async/await。这会导致更多线程启动。我只会让 ProcessSingle 成为一个普通的同步函数。
-
@PMF, @MickyD 但我无法摆脱
ProcessSingle内部对 DB 的异步调用的问题。所以唯一的方法是停止使用Parallel.ForEach并开始使用类似Task.WhenAll(/* call all ProcessSingle */)的东西?但是在那种情况下,没有内置的方法来限制并发线程(我记得,我见过一些自定义实现)? -
您最好使用 TPL DataFlow。你不仅可以使用
async/await,还可以throttle。 ;) -
不要将
Parallel.Foreah与 async/await 一起使用。它适用于 data 并行性 - 它的工作是将传入数据 partition 并将每个分区分配给 single 任务。你问的内容已经内置在ActionBlock<T>类中。
标签: c# async-await task task-parallel-library