一尘不染

不对TPL Task对象调用Dispose()是否可以接受?

c#

我想触发任务在后台线程上运行。我不想等待任务完成。

在.net 3.5中,我应该这样做:

ThreadPool.QueueUserWorkItem(d => { DoSomething(); });

在.net 4中,建议使用TPL。我看到的推荐的常见模式是:

Task.Factory.StartNew(() => { DoSomething(); });

但是,该StartNew()方法返回一个Task实现的对象IDisposable。推荐这种模式的人似乎忽略了这一点。有关该Task.Dispose()方法的MSDN文档说:

“在释放对任务的最后引用之前,请始终致电Dispose。”

您不能在任务完成之前对其进行调用处置,因此首先让主线程等待并进行调用处置会破坏在后台线程上执行任务的目的。似乎也没有任何可用于清除的完成/完成事件。

Task类上的MSDN页面对此没有评论,而书“ Pro C#2010 …”推荐了相同的模式,并且对任务处理没有评论。

我知道我是否只保留它,终结器最终会抓住它,但是当我做很多工作而忘了像这样的任务而终结器线程不堪重负时,这又会回来咬我吗?

所以我的问题是:

  • 是否可以接受不叫Dispose()Task在这种情况下类?如果是这样,为什么而且存在风险/后果?
  • 有没有讨论这个的文档?
  • 还是有适当的方法来处理Task我错过的对象?
  • 还是有另一种通过TPL做事忘了任务的方法?

阅读 290

收藏
2020-05-19

共1个答案

一尘不染

MSDN论坛中对此进行了讨论

Microsoft pfx团队的成员Stephen Toub这样说:

Task.Dispose之所以存在,是因为Task可能包装了在等待任务完成时使用的事件句柄,以防等待线程实际上不得不阻塞(而不是旋转或潜在地执行其正在等待的任务)。如果您所做的只是使用延续,那么该事件句柄将永远不会被分配

最好依靠终结来处理事情。

更新(2012年10月)
Stephen
Toub发布了一个博客,标题为“我需要处理任务吗?其中提供了更多详细信息,并说明了.Net
4.5中的改进。

总结:您不需要Task99%的时间处理对象。

放置对象的主要原因有两个:以及时,确定的方式释放不受管理的资源,以及避免运行对象的终结器的成本。这些都不适用于Task大多数时间:

  1. 从.Net 4.5开始,a唯一Task分配内部等待句柄(Task对象中唯一不受管理的资源)的时间是显式使用IAsyncResult.AsyncWaitHandleTask,并且
  2. Task对象本身没有终结; 该句柄本身使用终结器包装在一个对象中,因此,除非分配了该终结器,否则就无法运行终结器。
2020-05-19