代码之家  ›  专栏  ›  技术社区  ›  tmaj

Parallel.ForEachAsync任务与ValueTask

  •  1
  • tmaj  · 技术社区  · 4 年前

    我正在尝试 Parallel.ForEachAsync 编译器好心地通知我,主体是一个返回 ValueTask ,不是 Task

    Stopwatch sw = Stopwatch.StartNew();
    
    var numbers = Enumerable.Range(start: 0, count: 10);
    
    // Error: 'Task WorkAsync(int, CancellationToken)' has the wrong return type
    await Parallel.ForEachAsync(
        source: numbers,
        parallelOptions: new ParallelOptions{ MaxDegreeOfParallelism = 2 },
        body: WorkAsync); 
    
    async Task WorkAsync(int item, CancellationToken cancellationToken)
    {
        WriteLine($"Task {item} Start");
        await Task.Delay(TimeSpan.FromMilliseconds(100), cancellationToken).ConfigureAwait(false);
        WriteLine($"Task {item} End");
    }
    
    void WriteLine(string s) => Console.WriteLine($"{sw.ElapsedMilliseconds, 3} Thread{Thread.CurrentThread.ManagedThreadId}: {s}");
    

    快速搜索只得到使用lambda和多个语句的ForEachAsync示例: async (x, ct) => { ...; await ...; }

    我觉得

    body: async (item, cancellationToken) => await WorkAsync(item, cancellationToken).ConfigureAwait(false));
    

    明显比:

    body: WorkAsync);
    

    在这里的琐碎示例中,我显然可以更改worker方法的返回类型,但修改现有代码以返回似乎是不明智的 ValueTask s

    有没有一个明显的解决方案我错过了创建漂亮的代码 并行。用于异步 ?

    1 回复  |  直到 4 年前
        1
  •  2
  •   Theodor Zoulias    4 年前

    我不知道它是否更漂亮,但你可以考虑这样做:

    await Parallel.ForEachAsync(
        source: numbers,
        parallelOptions: new ParallelOptions { MaxDegreeOfParallelism = 2 },
        body: ToValueTaskResult<int>(WorkAsync));
    
    static Func<T, CancellationToken, ValueTask> ToValueTaskResult<T>(
        Func<T, CancellationToken, Task> function)
            => async (item, ct) => await function(item, ct).ConfigureAwait(false);
    

    老实说,我认为 Parallel.ForEachAsync API如果接受 Func<T, CancellationToken, Task> 委派为 body ,因为此API最常见的使用场景是网络上的高延迟I/O绑定操作。对于这些场景,从 Task ValueTask 在性能或内存效率方面几乎没有差异。降低API的一般可用性,以优化一些具有 IValueTaskSource -支持 身体 代表们,对我来说没有什么意义。但这就是我们现在拥有的,而且肯定比 what we had earlier ,所以我没有太多抱怨。