简体   繁体   English

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

[英]using TPL Dataflow, can I cancel all posts and then add one?

With the TPL Dataflow library, I would like to do something like this: 使用TPL Dataflow库,我想做这样的事情:

myActionBlock.Post(newValue, cancelAllPreviousPosts: true);

It appears that the cancellation token on ActionBlock cancels the whole thing; 似乎ActionBlock上的取消令牌取消了整个事情; I'd have to make a new ActionBlock if I set that one. 如果我设置了那个,我必须创建一个新的ActionBlock。 Is it possible to do a partial cancellation with ActionBlock? 是否可以使用ActionBlock进行部分取消?

Posts that have not been processed yet should not be attempted. 尚未尝试尚未处理的帖子。 It would be nice if there was some cancellation token available to check in the currently-executing post. 如果有一些取消令牌可用于检查当前正在执行的帖子,那将是很好的。

Take a look at BroadcastBlock<T> , which only holds the most recent item posted to it. 看一下BroadcastBlock<T> ,它只保存发布给它的最新项目。 You can put a broadcast block in front of an ActionBlock<T> . 您可以在ActionBlock<T>前面放置一个广播块。

While posting a new item to the broadcast block won't cancel the item currently being processed by the action block, it will overwrite any existing item already held by the broadcast block; 向广播块发布新项目不会取消动作块当前正在处理的项目,它将覆盖广播块已经存在的任何现有项目; in effect discarding any older messages not yet processed by the action block. 实际上丢弃了动作块尚未处理的任何旧消息。 When the action block completes its current item, it will take the most recent item posted to the broadcast block. 当动作块完成其当前项目时,它将将最新项目发布到广播块。

In addition to Monroe Thomas's answer it is important to understand that the ActionBlock following the BroadcastBlock needs it's BoundedCapacity limited to 1 or it will store and process every message of the broadcast block, even when it is still executing. 除了Monroe Thomas的回答之外,重要的是要理解BroadcastBlock之后的ActionBlock需要它的BoundedCapacity限制为1 ,否则它将存储和处理广播块的每个消息,即使它仍在执行。
A code example goes here: 代码示例如下:

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);
}

This results in the following: 这导致以下结果:

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

Enjoy! 请享用!
-Simon -Simon

There is nothing like this directly in TPL Dataflow, but I can see several ways how you could implement it yourself: 在TPL Dataflow中没有这样的东西,但是我可以通过几种方式看到你自己如何实现它:

  1. If you don't need to be able to treat the modified block as a normal Dataflow block (eg no support for LinkTo() ), then a simple way would to write a type that wraps ActionBlock , but whose items also contain a flag that says whether they should be processed. 如果您不需要能够将修改后的块视为普通的Dataflow块(例如,不支持LinkTo() ),那么一种简单的方法就是编写一个包装ActionBlock的类型,但其项目还包含一个标志说是否应该加工。 When you specify cancelAllPreviousPosts: true , all those flags are reset, so those items are going to be skipped. 当您指定cancelAllPreviousPosts: true ,将重置所有这些标志,因此将跳过这些项目。

    The code could look something like this: 代码看起来像这样:

     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 } 
  2. If you want to create something that's more composable and reusable, you could create a special block just for that cancellation. 如果您想创建更具可组合性和可重用性的东西,您可以创建一个特殊的块,仅用于取消。 You could implement that using thee BufferBlock s linked together, where the third one would have capacity of 1 and the second one unlimited capacity. 你可以使用连接在一起的BufferBlock实现它,其中第三个容量为1,第二个容量为无限容量。 This way, almost all the queued items would be in the second block, so you could perform cancellation just by swapping that block for a new one. 这样,几乎所有排队的项目都将位于第二个块中,因此您可以通过将该块交换为新块来执行取消。 The whole structure would be represented by Encapsulate() ing the first and the third block. 整个结构将由Encapsulate()表示第一个和第三个块。

    The issues with this approach is that the cancellation has a delay of 1 item (the one that's in the third block). 这种方法的问题是取消有一个项目的延迟(第三个块中的那个)。 Also, I didn't figure out a good interface for this. 另外,我没有找到一个很好的接口。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM