使用TPL Dataflow库,我想做这样的事情:
myActionBlock.Post(newValue, cancelAllPreviousPosts: true);
ActionBlock上的取消令牌似乎取消了整个事情;如果我设置了那个,我必须创建一个新的ActionBlock。是否可以使用ActionBlock进行部分取消?
不应尝试尚未处理的帖子。如果有一些取消令牌可用于检查当前正在执行的帖子,那就太好了。
答案 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中没有这样的内容,但我可以通过几种方式看到你自己如何实现它:
如果您不需要将修改后的块视为普通的数据流块(例如,不支持LinkTo()
),那么一种简单的方法就是编写一个包裹{{1}的类型1}},但其项目还包含一个标志,说明是否应该处理它们。当您指定ActionBlock
时,将重置所有这些标志,因此将跳过这些项目。
代码看起来像这样:
cancelAllPreviousPosts: true
如果您想创建更具可组合性和可重用性的内容,可以为该取消创建一个特殊块。你可以使用链接在一起的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个项目(第三个块中的那个)。另外,我没有想出一个好的界面。