使用TPL Dataflow,我可以取消所有帖子然后添加一个吗?

时间:2014-02-19 18:07:56

标签: c# task-parallel-library tpl-dataflow cancellationtokensource

使用TPL Dataflow库,我想做这样的事情:

myActionBlock.Post(newValue, cancelAllPreviousPosts: true);

ActionBlock上的取消令牌似乎取消了整个事情;如果我设置了那个,我必须创建一个新的ActionBlock。是否可以使用ActionBlock进行部分取消?

不应尝试尚未处理的帖子。如果有一些取消令牌可用于检查当前正在执行的帖子,那就太好了。

3 个答案:

答案 0 :(得分:4)

查看BroadcastBlock<T>,其中只包含最新发布的项目。您可以将广播块放在ActionBlock<T>前面。

向广播块发布新项目不会取消操作块当前正在处理的项目,它将覆盖广播块已经存在的任何现有项目;实际上丢弃了动作块尚未处理的任何旧消息。当动作块完成其当前项目时,它会将最新项目发布到广播块。

答案 1 :(得分:2)

除了Monroe Thomas的回答之外,重要的是要了解BroadcastBlock之后的ActionBlock需要它 BoundedCapacity限制为1 ,否则它将存储和处理每条消息广播块,即使它仍在执行 代码示例如下:

ActionBlock<int> ExecuteBlock = new ActionBlock<int>(async ThisNumber =>
{
  await Task.Delay(100);
  Console.WriteLine($">{ThisNumber}");
}, new ExecutionDataflowBlockOptions { BoundedCapacity = 1 });

BroadcastBlock<int> ThrottleBlock = new BroadcastBlock<int>(null);
ThrottleBlock.LinkTo(ExecuteBlock, new DataflowLinkOptions { PropagateCompletion = true });

for(int IX = 0; IX < 128; IX++)
{
  await ThrottleBlock.SendAsync(IX);
  await Task.Delay(10);
}

这导致以下结果:

>0
>6
>12
>20
>27
>34
>41
>48
>55
>62
>68
>75
>82
>88
>95
>101
>108
>115
>122
>127

享受!
-Simon

答案 2 :(得分:1)

在TPL Dataflow中没有这样的内容,但我可以通过几种方式看到你自己如何实现它:

  1. 如果您不需要将修改后的块视为普通的数据流块(例如,不支持LinkTo()),那么一种简单的方法就是编写一个包裹{{1}的类型1}},但其项目还包含一个标志,说明是否应该处理它们。当您指定ActionBlock时,将重置所有这些标志,因此将跳过这些项目。

    代码看起来像这样:

    cancelAllPreviousPosts: true
  2. 如果您想创建更具可组合性和可重用性的内容,可以为该取消创建一个特殊块。你可以使用链接在一起的class CancellableActionBlock<T> { private class Item { public T Data { get; private set; } public bool ShouldProcess { get; set; } public Item(T data) { Data = data; ShouldProcess = true; } } private readonly ActionBlock<Item> actionBlock; private readonly ConcurrentDictionary<Item, bool> itemSet; public CancellableActionBlock(Action<T> action) { itemSet = new ConcurrentDictionary<Item, bool>(); actionBlock = new ActionBlock<Item>(item => { bool ignored; itemSet.TryRemove(item, out ignored); if (item.ShouldProcess) { action(item.Data); } }); } public bool Post(T data, bool cancelAllPreviousPosts = false) { if (cancelAllPreviousPosts) { foreach (var item in itemSet.Keys) { item.ShouldProcess = false; } itemSet.Clear(); } var newItem = new Item(data); itemSet.TryAdd(newItem, true); return actionBlock.Post(newItem); } // probably other members that wrap actionBlock members, // like Complete() and Completion } 来实现它,其中第三个容量为1,第二个容量为无限容量。这样,几乎所有排队的项目都将位于第二个块中,因此您可以通过将该块交换为新块来执行取消。整个结构将由BufferBlock第一个和第三个块表示。

    这种方法的问题在于取消延迟了1个项目(第三个块中的那个)。另外,我没有想出一个好的界面。