取消数据流 ActionBlock 中的特定任务

Cancelling specific task in a dataflow ActionBlock

经过一整天的搜索后,我发现了一条评论,即 TPL 数据流取消是一种开箱即用的方式,可以取消整个块,而不是单个 task/item。我的主要障碍是,如果我的操作块同时并行 运行 10 个任务,我该如何取消任务 #5?那里有很多取消示例,但它们都只使用一个取消令牌,并最终取消任务 #5 和它之后的所有任务。

这是一些非常精简的代码。我猜我必须与 "i" 一起传递一些东西,但不清楚如何以编程方式创建唯一的取消标记,再加上在这个特定示例中我可能需要 1 到 10 个取消标记。即使我在鼠标单击方法中创建了一个取消令牌,我是否必须根据现有令牌检查它以确保它是唯一的?好迷茫。

            // Define the action block
        ActionBlock<int> throttle = new ActionBlock<int>(
            action: i=>DoStuff(i),
            dataflowBlockOptions: new ExecutionDataflowBlockOptions
            {
                MaxDegreeOfParallelism = 10,
                CancellationToken = cts.Token
            });

        // Create the work set (pretend it is a mouse-click method... it will not be a for loop)
        for (int i = 0; i < work.Length; i++)
        {
            Console.WriteLine($"{i:D3} : Posting Work Item {i}.");
            throttle.Post(i);
        }

如果您希望能够阻止它们,您需要一个取消令牌源列表,以便您可以分别向它们发送信号。如果你想要 work 中的每个元素一个,你可以 select 覆盖它:

var sources = work.Select( w => new CancellationTokenSource()).ToList();

然后,您必须修改DoStuff以检查令牌:

void DoStuff(int i, CancellationToken token)
{
    while (!token.IsCancellationRequested)
    {
        //Do the rest of the stuff
    }
}

而且调用的时候需要传递:

ActionBlock<int> throttle = new ActionBlock<int>(
    action: i=>DoStuff(i, sources[i].Token),  //<--- modified
    dataflowBlockOptions: new ExecutionDataflowBlockOptions
    {
        MaxDegreeOfParallelism = 10,
        CancellationToken = cts.Token
    });

然后要取消单个任务,请调用:

sources[i].Cancel();