C# 取消TPL数据流块的正确方法
我正在使用C# 取消TPL数据流块的正确方法,c#,task-parallel-library,tpl-dataflow,cancellationtokensource,C#,Task Parallel Library,Tpl Dataflow,Cancellationtokensource,我正在使用TPL块执行用户可能取消的操作: 我提出了两个选项,首先我取消整个块,但不取消块内的操作,如下所示: _downloadCts = new CancellationTokenSource(); var processBlockV1 = new TransformBlock<int, List<int>>(construct => { List<int> properties = GetPropertiesMethod(construc
TPL
块执行用户可能取消的操作:
我提出了两个选项,首先我取消整个块,但不取消块内的操作,如下所示:
_downloadCts = new CancellationTokenSource();
var processBlockV1 = new TransformBlock<int, List<int>>(construct =>
{
List<int> properties = GetPropertiesMethod(construct );
var entities = properties
.AsParallel()
.Select(DoSometheningWithData)
.ToList();
return entities;
}, new ExecutionDataflowBlockOptions() { CancellationToken = _downloadCts.Token });
\u downloadCts=new CancellationTokenSource();
var processBlockV1=新的转换块(构造=>
{
列表属性=GetPropertiesMethod(构造);
var实体=属性
.天冬酰胺()
.选择(含数据的剂量计)
.ToList();
返回实体;
},新的ExecutionDataflowBlockOptions(){CancellationToken=\u downloadCts.Token});
第二次我取消了内部操作,但不是块本身:
var processBlockV2 = new TransformBlock<int, List<int>>(construct =>
{
List<int> properties = GetPropertiesMethod(construct);
var entities = properties
.AsParallel().WithCancellation(_downloadCts.Token)
.Select(DoSometheningWithData)
.ToList();
return entities;
});
var processBlockV2=新的转换块(构造=>
{
列表属性=GetPropertiesMethod(构造);
var实体=属性
.AsParallel().WithCancellation(_downloadCts.Token)
.选择(含数据的剂量计)
.ToList();
返回实体;
});
据我所知,第一个选项将取消整个阻塞,从而关闭整个管道。我的问题是,它是否也会取消内部操作并处置所有资源(如有)(开放式StreamReaders等),还是选择第二个选项更好,因为这样我自己就可以确保取消并清理所有内容,然后我可以使用一些方法(铁路规划)要将提升的
操作取消异常
沿管道向下浮动,并在我想要的地方处理它?这两个选项并不等效
CancellationToken=\u downloadCts.Token
)将使processBlockV1
块丢弃当前在其缓冲区中的任何消息(其属性将变为0
),并停止接受新消息(调用其方法将始终返回false
)。但它不会停止处理当前正在处理的消息。这些将被完全处理,但不会传播到任何链接块。处理这些消息后,块将在取消状态下完成(其属性将变为已取消
)操作取消异常
,并被忽略。在特定示例中,将为所有construct
消息调用GetPropertiesMethod
方法,因此将对块的完成施加延迟。块的最终状态将是RanToCompletion
重要的是要知道,数据流块认真对待
完成的概念。在报告完成之前,他们将等待他们知道的一切停止运行。如果您确实希望他们提前完成并留下仍在运行的任务,您必须采取以下技巧。您应该将取消令牌传递给所有方法“取消”仅表示您请求结束任务。除非有人检查这个标志,否则它不会结束。这意味着DoSometheningWithData()
也应该检查\u downloadCts
,可能是\u downloadCts.throwifccancellationrequested()
。如果您处理的是一次性资源,请将它们放在using()
块中,以确保它们被丢弃。TPL的真正功能是从几个不同的构建块构建管道。因此,它的设计方式是,每当ISourceBlock
结束时(无论出于何种原因),它都可以通知所有链接的ITargetBlock
s。为了这样工作,你必须在你的<代码>链接>代码> >调用:<代码>新的DATAFOLLKOOREST { ExpActudioPrime=真} /代码>我的建议是考虑取消制作人部分,而不是CuffelHi,特奥多尔,谢谢你的回答!基于此信息,我认为我的案例的最佳方案是在块定义本身中使用newexecutiondataflowblockoptions(){CancellationToken=\u downloadCts.Token}
,然后将相同的令牌传递给内部处理方法以停止它正在做的任何事情。我对它进行了测试,相同的令牌能够取消块,然后它在内部方法中再次“激发”。我的问题是-它能保证它在内部方法中总是第二次开火吗?嗨@niks!是,它将为当前正在处理的所有项目激发。当前处理的项的数量主要取决于传递到块的构造函数中的设置。默认情况下是1。我明白了。还有一件有趣的事,如果我使用Try library()和wrap异常,这样它们就可以沿着管道传递,如果CancelationToken
在TransformBlock
上和内部都被激发,OperationCanceledException
即使在内部处理方法中抛出,也不会向下浮动到最后一个块。不确定这是否是一个问题,但我认为这是实施此类解决方案时应该注意的事项。@niks是的,我也注意到了这一点,而且似乎不太符合逻辑。实际上,虽然相同的取消令牌通常会传递给管道的所有块,因此,在大多数情况下,这种意外行为都不会出现。嗯……我现在在想,如果我将相同的cancelationToken
传递给所有块,并且我使用前面提到的Try
库来包装每个异常并将其作为一个简单的值在管道中浮动,设置新的DataflowLinkOptions{PropagateCompletion=true}
将块链接在一起时有任何