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

如何中断Parallel.ForEachAsync循环,而不是取消它?

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

    在.NET 5中 Parallel.ForEach 你可以使用 ParallelLoopState.Break() 方法来停止处理其他迭代。允许当前用户完成处理。

    但是新的.NET 6 Parallel.ForEachAsync 没有 ParallelLoopState 上课,这样我们就不能像用 并行循环 。那么,有没有一种方法可以在中执行相同的中断功能 ForEachAsync ? CancellationToken 传递给func,我认为这不是正确的方式,因为你没有试图取消正在运行的循环,而是阻止了额外的迭代开始。

    类似于此功能,但适用于异步版本:

    int count = 0;
    Parallel.ForEach(enumerateFiles, new ParallelOptions() { CancellationToken = cancellationToken},
        (file, state) =>
        {
            Interlocked.Increment(ref count);
            if (count >= MaxFilesToProcess)
            {
                state.Break();
            }
    ...
    

    作为一种变通方法,我可能会使用 .Take([xx]) TSource 在它被传递到并行循环之前,但这可能不是一个复杂条件的选项。

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

    异步API Parallel.ForEachAsync 不提供 Stop / Break 其同步对手的功能。

    复制此功能的一种方法是使用 bool 标志与 TakeWhile LINQ操作员:

    bool breakFlag = false;
    await Parallel.ForEachAsync(
        source.TakeWhile(_ => !Volatile.Read(ref breakFlag)),
        async (item, ct) =>
    {
        // ...
        if (condition) Volatile.Write(ref breakFlag, true);
        // ...
    });
    

    这个 并行。用于异步 不主动缓冲源序列中的元素,如 Parallel.ForEach 是的,所以只要满足条件,就不会再启动异步操作。

    情况是 source 是异步可枚举对象( IAsyncEnumerable<T> ),有一个兼容的 TakeWhile 中具有相同功能的操作员 System.Linq.Async 包裹

    至少今天没有(.NET 6)。这种行为是 not documented or guaranteed