C#:使用Parallel.ForEach和异步操作

时间:2018-09-14 06:16:38

标签: c# async-await task task-parallel-library

我正在尝试使用asp.net core 2.1实现自托管的Web服务,并陷入了实现后台长时间执行任务的问题。

由于每个ProcessSingle方法的高CPU负载和时间消耗(在下面的代码段中),我想限制同时执行任务的数量。但是我可以看到Parallel.ForEach中的所有任务几乎立即开始,尽管我设置了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)中,不是吗?

有人可以描述一下,如何以正确的方式实施它吗?

更新

由于我的问题中存在某种歧义,因此有必要关注以下主要方面:

  1. ProcessSingle方法中包含三个部分:

    • 从数据库异步获取数据

    • 进行长时间的高CPU负载数学计算

    • 将结果保存到数据库异步

  2. 问题由两个单独的部分组成:

    • 如何减少CPU使用率(例如,通过运行不超过三个数学同时计算)?

    • 如何保持ProcessSingle方法的结构-由于异步DB调用而使它们保持异步。

希望现在会更清楚。

P.S。已经给出了合适的答案,它可以工作(特别是感谢@MatrixTai)。编写此更新用于一般性说明。

1 个答案:

答案 0 :(得分:0)

如果您更熟悉“传统”并行处理概念,请像这样重写您的ProcessSingle()方法:

public static void ProcessSingle(MyInputData inputData)
{
    var dbData = GetDataFromDb(); // get data from DB async using Dapper
    // some lasting processing (sync)
    SaveDataToDb(); // async save processed data to DB using Dapper
}

当然,您最好也以类似的方式更改Work()方法。