我需要处理一个Task吗?

18

我很喜欢使用System.Threading.Tasks。然而,我看到的许多代码示例都类似于以下内容:

Dim lcTask = Task.Factory.StartNew(Sub() DoSomeWork())
Dim lcTaskLong = Task.Factory.StartNew(Sub() DoSomeWork(), TaskCreationOptions.LongRunning)
Task.WaitAll(lcTask, lcTaskLong)

这就是示例的全部内容。
Tasks 实现了 IDisposable 接口,所以我显然应该将其释放,但如果我只想 "Fire and Forget"(启动并忘记),那怎么办?

如果我不释放,会泄漏线程/句柄/内存/业力吗? 我是否使用 tasks 的方式有误?(应该仅使用委托而不是涉及 tasks ?)

我可以在 ContinueWith() 中进行释放吗?(这似乎像在玩俄罗斯轮盘。)

1个回答

22

通常的经验法则是对所有IDisposable实现都调用Dispose(),但在TaskTask<T>中,更好的做法是让终结器来处理。

Task实现IDisposable主要是由于内部的WaitHandle。这是为了使任务连续工作正常所必需的,仅在任务存在连续时才会使用。如果没有连续,任务的Dispose方法就没有真正的效果-所以在这种情况下,不需要Dispose。

话虽如此,在大多数情况下,当有一个任务连续时,编写可以正确调用Dispose()的代码通常非常困难。使用语句通常不能与任务实例一起使用,因为任务调用通常是异步的性质。很容易在早期处置任务,尤其是在使用using语句时。

如果在您的情况下,能够相对简单地保持对任务的引用并正确调用Dispose(),那么我会这样做。然而,如果这将导致您的逻辑变得更加复杂,我通常会假装Task没有IDisposable,并允许它在Task的终结器中清理。

有关更多详细信息,建议阅读MSDN论坛上的此线程,其中Stephen Toub详细描述了为什么Task实现IDisposable,并提供了类似于我上面建议的指导。


1
并且... [CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000", Justification = "https://dev59.com/D2025IYBdhLWcg3weF6S")] - GarethOwen
3
根据你提供的链接中的 Stephen Toub 帖子,我认为你在说“[等待句柄]是必需的,以使任务继续正常工作,并且只有在任务上存在继续时才使用。如果没有继续,Task 的 Dispose 方法就没有真正的效果……”时恰好相反。但根据 Toub 的说法,“Task.Dispose 存在是由于 Task 可能会包装事件句柄……如果你只是使用继续,那么该事件句柄将永远不会被分配,Dispose 的价值大大降低。”即与你上面所暗示的相反。 - Daniel Earwicker
这里有有用的分析:https://devblogs.microsoft.com/pfxteam/do-i-need-to-dispose-of-tasks/ - Marťas

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