多源任务取消

6
我目前有一个应用程序,其中我创建了一系列任务,这些任务按顺序执行,具有可以在任务之间中断执行的取消源(即在安全终止点)。我目前仅在管理类被处理时使用此取消源,作为一种清洁且快速中止执行的方式。
现在,我有一个用例,我希望创建一个自动超时以取消任务序列,以防操作员未能及时响应(一些任务等待操作员与物理机构交互)。同时,如果管理类被处理,我仍然需要支持取消。我找到了CancellationTokenSource.CreateLinkedTokenSource,听起来像是我需要的,但我有一些顾虑:
  1. 多个任务系列可以并行执行,因此我需要为超时取消创建一个新的CancellationTokenSource,并为每个开始的任务系列创建一个相关联的链接源。CancellationTokenSource实现IDisposable,这意味着我需要保留取消源并在最后一个任务完成或任何子任务被取消或出错时进行Dispose。即使有匿名方法闭包的帮助,这似乎仍然很笨拙(取消源仍然在传递)。

  2. 我还需要防止在计时器到期之前取消源被Dispose(以便我不会取消已经Dispose的源)。这是潜在的竞争条件,所以我需要添加适当的锁定。这看起来也很笨拙,增加了显著的复杂性(未来的维护成本),并使单元测试更具挑战性(竞争条件很难可靠地引发)。

我是否正在正确的道路上,或者有没有更简单的方法来解决这个问题?

1个回答

9
您问题的核心似乎与在您的Task和/或CancellationTokenSource对象上调用Dispose()有关。在这种情况下,我建议您不要调用这些对象的Dispose,这应该可以大大简化您的设计。
作为理由,我将引用此线程。特别是,负责Task的PM Stephen Toub建议您:

如果根据代码结构轻松且正确地进行处理,则积极处置。如果您开始不得不进行奇怪的曲折以进行Dispose(或在任务的情况下,使用其他同步来确保安全地进行Dispose,因为Dispose只能在任务完成后使用一次),那么最好依赖终结来处理事情。

这听起来像他在最后描述的确切情况-您正在尝试进行奇怪的曲折,以调用这些对象的Dispose()

1
我一直被强烈灌输,应该始终调用IDisposable对象上的Dispose方法,因此即使得到Stephen Toub的认可,我也会犹豫。实际上,CancellationTokenSource.Dispose()的MSDN文档明确指出,您应该始终调用Dispose(); 这可能只是通用措辞,但这表明.NET团队没有在这个类中留下Dispose()“可选”的合同义务。http://msdn.microsoft.com/en-us/library/dd321505.aspx - Dan Bryant
@Dan: 我知道 - 但是请记住,只要实现了IDisposable接口的对象,如果你不调用Dispose()方法,它会被终结器自动"清理"。我认为你应该总是尽量调用任何IDisposable对象的Dispose()方法,但在某些情况下,这样做可能会比仅仅依靠终结器更糟糕。TPL就是其中一个模糊的领域 - 在我看来,通常最好不要Dispose() TPL对象,因为执行这些操作很可能引入维护问题和错误。 - Reed Copsey
我完全理解你的观点(从实际、完成工作的角度来看),这也可能是我最终会采取的做法,但这种“两害相权取其轻”的情况让我感到不安。然而,现在的反对更多是哲学上的而非技术上的,任何工程都无法解决这个问题。 - Dan Bryant
1
@Dan:我知道你的意思——不“有意地”处理某些东西让我感到很困扰——但说到底,这只是小恶之一。 - Reed Copsey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接