【IT168 技术】在.NET 4.0中,Task类暴露了IDisposable接口。Task可被回收(disposable)是为了清理IAsyncResult接口中AsyncWaitHandle属性暴露的等待句柄(wait handle)。在.NET 4.0中,等待句柄只有在读取AsyncWaitHandle属性,或者使用Task.WaitAll、Task.WaitAny时才会被创建,其他情况调用Task.Dispose都是多余的。
遗憾的是,.NET 4.0中的Task在处理ObjectDisposedException时显得过于武断:一旦调用Dispose释放等待句柄之后,即使其他属性与之毫无联系,剩余对象也会变得不稳定。
那么在.NET 4.0中是否应该调用Task.Dispose?
不应该,除非遇到以下情况:
整个Task不会被缓存;
等待句柄是通过调用Task.WaitAll、Task.WaitAny,或是读取IAsyncResult.AsyncWaitHandle创建而成;
Task上没有其他任务或线程处于等待状态。
其实,即使所有的条件都满足,你也不用做什么,因为终结器 (finalizer)在清理等待句柄方面已经做了相同高效的工作。所以,除非你看到一些性能问题,否则你也许可以仍然不用回收task。
.NET 4.5核心中的改动
在.NET 4.5中,只有显式读取IAsyncResult.AsyncWaitHandle时,内部等待句柄才会被创建。其他部分,包括Task.WaitAll和Task.WaitAny都进行了重新设计,它们不再需要等待句柄。另外,随着语言中对async/await的支持,IAsyncResult在大部分场景中甚至都不再需要。
.NET 4.5中Task的另外一个改动是task在释放之后仍然可用。按照Stephen Toub的说法,“现在,即使Task释放之后也可以使用它的所有公开成员,并且它们使用起来就和释放之前一样。唯一一个不能使用的成员是IAsyncResult.AsyncWaitHandle,因为它是Task实例真正释放的部分。如果试图在Task释放后访问该属性,它会抛出一个ObjectDisposedException异常。”
虽然在.NET 4.5中调用Task.Dispose变得更加安全,但是几乎没有理由需要这么做。
针对.NET 4.5 Metro的特殊规则
Stephen Toub接着提到Task.Dispose在“.NET Metro风格应用程序”框架中甚至并不存在。要注意的是,目前关于此项设计变更的信息还未在WinRT的Task文档中更新反映。
从函数返回Task
在另外一篇题为“是否应当为同步方法暴露异步包装?“的文章中,Stephen深度探讨了从函数返回Task对象的话题。我们推荐你阅读全篇文章,而如果你时间不充裕,可以阅读以下的总结部分:
我认为只有那些异步方法比对应的同步方法拥有可扩展性(scalability)优势时才应当被暴露。异步方法不应当为了单纯地减轻负载(offloading)而进行暴露,因为这类优势可以通过使用专门用于异步执行同步方法的功能轻松实现,如使用Task.Run。
查看英文原文:Changes and Guidance for the Task Parallel Library in .NET 4.5