我正在使用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 });
第二个我取消内部操作,但不取消块本身:
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;
});
据我所知,第一个选项将取消整个块,从而关闭整个管道。我的问题是是否还会取消内部操作并处置所有资源(如果有的话)(打开StreamReaders等),还是最好选择第二个选项,否则我本人可以确保取消并清理所有内容,然后再使用一些方法(铁路编程)将升高的OperationCanceledException
沿管道浮动并在需要的地方处理?
这两个选项不相同。
第一个选项(CancellationToken = _downloadCts.Token
)将使processBlockV1
块放弃其缓冲区中当前存在的任何消息(其InputCount
属性将变为InputCount
),并停止接受新消息(调用其0
方法将始终返回Post
。它不会停止处理当前正在处理的消息。这些将被完全处理,但不会传播到任何链接的块。处理完这些消息后,该块将以取消状态完成(其Post
false
属性将变为Completion
)。
第二个选项(取消内部操作)对整个块均无效。 Dataflow块可以容忍从其处理功能中抛出的所有Completion
,而只需忽略出现故障的项目并继续进行下一项。因此,在取消令牌后,所有发布的消息仍将被处理,并且该区块将继续接受更多消息。它只是不会传播任何东西到它的链接块,因为所有项目都会抛出.Status
并被忽略。在特定示例中,将为所有.Status
消息调用Canceled
方法,因此将对块的完成施加延迟。块的最终状态将是OperationCanceledException
。
重要的是要知道数据流模块正在认真对待OperationCanceledException
的概念。他们将等待所知道的一切停止运行,然后再报告完成。如果您确实希望它们过早完成并留下仍在运行的任务,则必须执行OperationCanceledException
之类的技巧。