在 C#8 IAsyncEnumerable<T> 中并行化收益返回

God*_*ent 6 c# asynchronous iasyncenumerable

我有一个返回异步枚举器的方法

    public async IAsyncEnumerable<IResult> DoWorkAsync()
    {
        await Something();
        foreach (var item in ListOfWorkItems)
        {
            yield return DoWork(item);
        }
    }
Run Code Online (Sandbox Code Playgroud)

还有来电者:

    public async Task LogResultsAsync()
    {
        await foreach (var result in DoWorkAsync())
        {
            Console.WriteLine(result);
        }
    }
Run Code Online (Sandbox Code Playgroud)

因为DoWork这是一项昂贵的操作,我更喜欢以某种方式并行化它,所以它的工作原理类似于:

    public async IAsyncEnumerable<IResult> DoWorkAsync()
    {
        await Something();
        Parallel.ForEach(ListOfWorkItems, item =>
        {
            yield return DoWork(item);
        });
    }
Run Code Online (Sandbox Code Playgroud)

但是我无法从内部进行收益回报Parallel.Foreach,所以想知道解决这个问题的最佳方法是什么?

返回结果的顺序并不重要。

谢谢。

编辑:抱歉,我遗漏了一些代码DoWorkAsync,它确实在等待我只是没有将其放入上面的代码中,因为这与问题不太相关。现已更新

Edit2: DoWork在我的例子中主要是 I/O 限制,它从数据库读取数据。

Joh*_*lay 2

AsParallel正如canton7 所建议的,您可以使用Parallel.ForEach.

这可以在标准循环中使用,foreach您可以在其中产生结果:

public async IAsyncEnumerable<IResult> DoWorkAsync()
{
    await Something();
    foreach (var result in ListOfWorkItems.AsParallel().Select(DoWork))
    {
        yield return result;
    }
}
Run Code Online (Sandbox Code Playgroud)

正如 Theodor Zoulias 所提到的,返回的可枚举实际上根本不是异步的。

如果您只是需要使用 this 来消费它await foreach应该不是问题,但更明确地说,您可以返回IEnumerable并让调用者并行化它:

public async Task<IEnumerable<Item>> DoWorkAsync()
{
    await Something();
    return ListOfWorkItems;
}

// Caller...
Parallel.ForEach(await DoWorkAsync(), item => 
{
    var result = DoWork(item);
    //...
});
Run Code Online (Sandbox Code Playgroud)

尽管如果需要在多个地方调用它可能不太容易维护