将异步方法传递到 Parallel.ForEach

Dmi*_*nov 4 c# async-await parallel.foreach

我正在阅读这篇关于Parallel.ForEach“Parallel.ForEach 与传入异步方法不兼容”的文章。

所以,为了检查我写了这段代码:

static async Task Main(string[] args)
{
    var results = new ConcurrentDictionary<string, int>();

    Parallel.ForEach(Enumerable.Range(0, 100), async index =>
    {
        var res = await DoAsyncJob(index);
        results.TryAdd(index.ToString(), res);
    });         

    Console.ReadLine();
}

static async Task<int> DoAsyncJob(int i)
{
    Thread.Sleep(100);
    return await Task.FromResult(i * 10);
}
Run Code Online (Sandbox Code Playgroud)

此代码同时填充results字典。

顺便说一下,我创建了一个类型的字典,ConcurrentDictionary<string, int>因为万一我ConcurrentDictionary<int, int>在调试模式下探索它的元素时,我看到元素是按键排序的,我认为因此添加了 elenents。

所以,我想知道我的代码是否有效?如果它“与传入异步方法不兼容”,为什么它运行良好?

Pan*_*vos 7

这段代码之所以有效,只是因为DoAsyncJob它并不是真正的异步方法。async不会使方法异步工作。等待由 返回的Task.FromResult已完成任务也是同步的。async Task Main不包含任何异步代码,这会导致编译器警告。

一个演示如何Parallel.ForEach不适用于异步方法的示例应该调用真正的异步方法:

    static async Task Main(string[] args)
    {
        var results = new ConcurrentDictionary<string, int>();

        Parallel.ForEach(Enumerable.Range(0, 100), async index =>
        {
            var res = await DoAsyncJob(index);
            results.TryAdd(index.ToString(), res);
        });  
        Console.WriteLine($"Items in dictionary {results.Count}");
    }

    static async Task<int> DoAsyncJob(int i)
    {
        await Task.Delay(100);
        return i * 10;
    }
Run Code Online (Sandbox Code Playgroud)

结果将是

Items in dictionary 0
Run Code Online (Sandbox Code Playgroud)

Parallel.ForEach没有接受 a 的重载Func<Task>,它只接受Action委托。这意味着它不能等待任何异步操作。

async index被接受是因为它隐含地是一个async void委托。就其Parallel.ForEach而言,它只是一个Action<int>.

结果是Parallel.ForEach触发了 100 个任务并且从不等待它们完成。这就是为什么当应用程序终止时字典仍然是空的。