C# 对TPL任务对象不调用Dispose()是否可以接受?
我想触发一个在后台线程上运行的任务。我不想等待任务完成 在.net 3.5中,我会这样做:C# 对TPL任务对象不调用Dispose()是否可以接受?,c#,.net,multithreading,dispose,task-parallel-library,C#,.net,Multithreading,Dispose,Task Parallel Library,我想触发一个在后台线程上运行的任务。我不想等待任务完成 在.net 3.5中,我会这样做: ThreadPool.QueueUserWorkItem(d => { DoSomething(); }); 在.NET4中,建议使用TPL。我看到的常见模式是: Task.Factory.StartNew(() => { DoSomething(); }); 但是,该方法返回的对象实现了IDisposable。这 似乎被推荐这种模式的人忽略了。有关该方法的MSDN文档说明: “在释放对任
ThreadPool.QueueUserWorkItem(d => { DoSomething(); });
在.NET4中,建议使用TPL。我看到的常见模式是:
Task.Factory.StartNew(() => { DoSomething(); });
但是,该方法返回的对象实现了IDisposable
。这
似乎被推荐这种模式的人忽略了。有关该方法的MSDN文档说明:
“在释放对任务的最后引用之前,始终调用Dispose。”
在任务完成之前,您无法对其调用dispose,因此让主线程等待并调用dispose将首先挫败在后台线程上执行任务的意义。似乎也没有任何可用于清理的已完成/已完成事件
Task类上的MSDN页面没有对此进行评论,而《Pro C#2010…》一书也推荐了相同的模式,并且没有对任务处理进行评论
我知道如果我离开它,终结器最终会抓住它,但是当我做很多火&忘记这样的任务,终结器线程被淹没时,它会回来咬我吗
因此,我的问题是:
- 在这种情况下,不调用
类上的Task
是否可以接受?如果是,为什么会有风险/后果Dispose()
- 是否有任何文件对此进行了讨论李>
- 或者有没有合适的方法来处理我错过的
对象任务
- 或者,有没有其他方法可以通过TPL完成fire&forget任务
- 关于这一点有一个讨论
微软pfx团队成员Stephen Toub说:
任务。由于任务原因,存在Dispose
潜在地包装事件句柄
在等待任务完成时使用
在等待的情况下完成
线程实际上必须阻塞(如
反对旋转或潜在的
执行它正在等待的任务)。
如果你所做的只是使用
continuations,该事件句柄将
永远不要被分配
…
可能最好依靠最终定稿来处理事情 更新(2012年10月)
Stephen Toub发布了一个名为的博客,提供了更多细节,并解释了.NET4.5的改进 总之:99%的时间你不需要处理
Task
对象
处置对象有两个主要原因:及时、确定地释放非托管资源,以及避免运行对象终结器的成本。在大多数情况下,这两种方法都不适用于任务
:
任务
唯一分配内部等待句柄(任务对象中唯一的非托管资源)的时间是显式使用任务的IAsyncResult.AsyncWaitHandle
时,以及任务
对象本身没有终结器;句柄本身包装在一个带有终结器的对象中,因此除非分配了它,否则没有终结器可以运行这与Thread类的问题类型相同。它使用5个操作系统句柄,但不实现IDisposable。对于最初的设计人员来说,这是一个很好的决定,当然调用Dispose()方法的合理方法很少。您必须首先调用Join() Task类为此添加了一个句柄,即内部手动重置事件。哪种是最便宜的操作系统资源。当然,它的Dispose()方法只能释放一个事件句柄,而不能释放线程使用的5个句柄
请注意,您应该对任务的IsFaulted属性感兴趣。这是一个相当丑陋的话题,你可以在这篇文章中读到更多。一旦你正确地处理了这个问题,你的代码中也应该有一个很好的位置来处理任务。我很想看到有人对这篇文章中显示的技术进行权衡: 看起来一个简单的扩展方法将处理与任务交互的所有琐碎情况,并能够对其调用dispose
public static void FireAndForget<T>(this Action<T> act,T arg1)
{
var tsk = Task.Factory.StartNew( ()=> act(arg1),
TaskCreationOptions.LongRunning);
tsk.ContinueWith(cnt => cnt.Dispose());
}
公共静态无效FireAndForget(本行动法案,T arg1)
{
var tsk=Task.Factory.StartNew(()=>act(arg1),
TaskCreationOptions.LongRunning);
tsk.ContinueWith(cnt=>cnt.Dispose());
}
谢谢,很有趣。不过,这与MSDN文档背道而驰。微软或.net团队是否有任何官方说法表明这是可以接受的代码。在讨论结束时还提出了一点,“如果未来版本中的实现发生变化怎么办”事实上,我刚刚注意到该线程中的回答者确实在微软工作,似乎在pfx团队中,所以我想这是某种形式的官方回答。但有人建议,在所有情况下,这种做法都是行不通的。如果存在潜在泄漏,我是否最好恢复到我知道是安全的ThreadPool.QueueUserWorkItem?是的,非常奇怪的是,有一个您可能不会调用的Dispose。如果你看一下这里和这里的样本,它们不是在处理任务。然而,MSDN中的代码示例有时会演示非常糟糕的技术。这个问题的答案同样适用于Microsoft。@Simon:(1)您引用的MSDN文档是通用的建议,特定情况下有更具体的建议(例如,当使用BeginInvoke
在UI线程上运行代码时,不需要在WinForms中使用EndInvoke
)。(2) 斯蒂芬·图布(Stephen Toub)作为有效使用PFX(例如on)的常客而广为人知,因此,如果有人能提供良好的指导,那么他就是。注意他的第二段:有时把事情留给最终定稿人会更好。相关:(参见)当然,这无法处理由ContinueWith
返回的任务实例,但参见Stephen Toub的引用是公认的答案:如果没有任何东西执行阻塞,则没有任何东西可处理