【发布时间】:2020-02-08 08:09:06
【问题描述】:
当数据流块当前未处理消息并且已保证不再处理任何消息时,数据流块被视为已完成。
在我的情况下,这种行为并不理想。我希望能够随时取消作业,但是处理每个单独的操作需要很长时间。因此,当我取消令牌时,效果不会立即生效。我必须等待当前处理的项目完成。我无法直接取消操作,因为我使用的 API 是不可取消的。我可以做任何事情使该块忽略当前正在运行的操作并立即完成吗?
这是一个演示我的问题的示例。令牌在 500 毫秒后被取消,每个动作的持续时间为 1000 毫秒:
static async Task Main()
{
var cts = new CancellationTokenSource(500);
var block = new ActionBlock<int>(async x =>
{
await Task.Delay(1000);
}, new ExecutionDataflowBlockOptions() { CancellationToken = cts.Token });
block.Post(1); // I must wait for this one to complete
block.Post(2); // This one is ignored
block.Complete();
var stopwatch = Stopwatch.StartNew();
try
{
await block.Completion;
}
catch (OperationCanceledException)
{
Console.WriteLine($"Canceled after {stopwatch.ElapsedMilliseconds} msec");
}
}
输出:
1035 毫秒后取消
所需的输出将是约 500 毫秒后的取消。
【问题讨论】:
-
您要求
Thread.Abort行为,而不是取消。 TPL 与合作取消一起工作。这是避免泄漏和同步问题所必需的。如果您希望延迟取消,请将 CancellationToken 传递给它。如果您希望管道中的所有块突然取消,请将 CancellationToken 传递给所有块。 -
我没有投反对票,但您正在尝试以不应该工作的方式使用该库。您不需要这样做 - Dataflow 来自 CCR,这是一个 Robotics 平台 - 一个具有相当严格的实时要求的库。在这些情况下,中止是一种诅咒——它们使飞机坠毁,机械臂与人类相撞。与合作取消相比,
Thread.Abort的方式抛出异常非常慢。 应用程序代码必须符合域,而不是相反。 -
这意味着 action 必须注意在需要取消时快速退出,而不是阻止。无论如何可以做什么?扔掉并可能半途而废?还是返回并执行在后台运行的任务?如果动作需要提前退出,则必须正确编写并检查取消令牌本身
-
@PanagiotisKanavos
await Task.Delay(1000)只是一个例子。我的实际工作量是一个不可取消的 API。它不接受CancelationToken参数,我无法在它启动后停止或中止它。在取消请求的情况下我想要发生的是忽略当前正在运行的工作项。我已经不在乎了,为什么还要等呢?不用担心,没有飞机会坠毁,也不会有人受到失灵机械臂的伤害。我的应用程序只是使用慢速打印机打印一些发票。 :-)。 -
我已经发布了一个建议,如果我没有误解这个问题,那就是。正如你已经知道的那样,我还不是很精通使用 async/await,但我觉得这可以使用 async/await 进一步简化 ;-)。如果您可以编辑我的帖子以正确使用 async/await 以达到相同的效果,请随时改进!
标签: c# cancellation tpl-dataflow