【问题标题】:Do I need to check the cancellationtoken in Parallel.Foreach我是否需要检查 Parallel.Foreach 中的取消令牌
【发布时间】:2019-09-19 08:17:10
【问题描述】:

这是How to: Cancel a Parallel.For or ForEach Loop的文档

我已经编写了一个使用该功能的方法:

static async Task SpreadCheer(CancellationToken cancellationToken)
{
    cancellationToken.ThrowIfCancellationRequested();
    var friends = await GetFriends();

    Parallel.ForEach(friends,
            new ParallelOptions
                { CancellationToken = cancellationToken},
            friend=>
            {
                SendCake(friend);
                // (*1)
            });
}

在文档中的示例中,在每个循环的最后一行,(*1)。它添加了一个cancellationToken.ThrowIfCancellationRequested

这让我很困惑,因为那我为什么将它传递给并行选项。如果令牌被取消,我也可以see thatParallel.Foreach 在处理项目后已经抛出。如果令牌从一开始就取消,它也会在开始任何循环之前立即抛出。这也是我运行和测试此代码的经验,不需要显式抛出取消。

我是否需要(我什至应该)将cancellationToken.ThrowIfCancellationRequested 放置在(*1) 的位置?

当然文档有一个例子,但它没有明确提到它,如果有一个单元测试涵盖了这个,它不需要通过该测试的代码。据我所知。


额外的上下文:

关于取消的一些上下文。取消的原因是我有一个 Azure WebJob 到 SpreadCheer,Spreading Cheer 可能需要一段时间,如果 Azure 向我发送关闭信号,我想优雅地停止传播欢呼。我不想弄乱任何人的蛋糕。

【问题讨论】:

  • 与此同时,我决定不在位置 (*1) 放置任何东西,因为我无法编写基于添加该代码的从红色变为绿色的测试。

标签: c# parallel.foreach


【解决方案1】:

您应该在操作本身中使用token.ThrowIfCancellationRequested() 的原因是,并行操作将继续执行直到最后。

Parallel.ForEach 只在操作的开始和结束时检查令牌,而不是介于两者之间。

因此,如果您有很多并行操作或长时间运行的操作,您需要自己检查操作中的取消。

您可以使用以下 sn-p 验证行为,无论是否使用 token.ThrowIfCancellationRequested()

internal class Program
{
    private static void Main()
    {
        var source = new CancellationTokenSource();

        var token = source.Token;

        Task.Run(() =>
        {
            Parallel.ForEach(Enumerable.Range(1, 100), new ParallelOptions {CancellationToken = token},
                i =>
                {
                    for (var y = 0; y < 100; y++)
                    {
                        token.ThrowIfCancellationRequested();
                        Thread.Sleep(1000);
                        Console.WriteLine($"{i} {y}");
                    }
                });
        }, token);

        Console.WriteLine("press return to cancel...");
        Console.ReadLine();

        source.Cancel();

        Console.WriteLine("press return to exit...");
        Console.ReadLine();
    }
}

【讨论】:

  • 感谢您的回答。因此,您可以在并行操作期间添加附加检查,以防在更细粒度级别存在自然停止点。文档中的检查是完全不必要的(并且具有误导性),但只是暗示可以在一个实际值得它的示例中完成它,例如你的。我想一定是这样的。
猜你喜欢
  • 2014-06-03
  • 1970-01-01
  • 1970-01-01
  • 2019-12-29
  • 2020-09-29
  • 2011-03-16
  • 2018-03-29
  • 1970-01-01
  • 2016-11-02
相关资源
最近更新 更多