【转】1.B(译).NET4.X并行任务Task需要释放吗?

传送门:异步编程系列目录……

 

摘要:本博文解释在.NET 4.X中的Task使用完后为什么不应该调用Dispose()。并且说明.NET4.5对.NET4.0的Task对象进行的部分改进:减轻Task对WaitHandle对象的依赖,并且增强在释放了Task后对其成员的可访问性。

 

我多次获得这样一个问题:

         “Task实现了IDisposable接口并且公开Dispose()方法,这是否意味着我们要对所有的任务进行释放吗?”

 

概述

1.         这是我对该问题的简要回答:

“不是,不用释放你持有的Task。”

 

2.         这是我对该问题的中篇回答:

“不是,不用释放你持有的Task,除非性能报告或可伸缩性测试报告显示你需要释放Task以满足你的性能目标。如果你发现一个需要被释放的Task,必须100%确保在你的释放点处该Task已经完成并且没有被其他地方在使用。”

 

3.         下面,你可以找一个休闲的阅读时间,这是我对该问题的长回答:

 

为什么要调用Task的Dispose()?

.NET Framework设计指南中指出:一个类型如果持有其它实现过IDisposable接口的资源时,其自身也应该实现IDisposable接口。在Task内部,可能会分配一个WaitHandle对象用于等待任务完成。WaitHandle实现IDisposable接口因为它持有SafeWaitHandle内核等待句柄,所以Task实现了IDisposable接口。如果不主动释放SafeWaitHandle句柄,最终终结器也会将其清理,但是不能对此资源立即清理并且还将此清理工作负荷遗留给系统。通过给Task实现IDisposable接口,我们可以让开发人员能主动及时的对资源进行释放。

 

问题

         如果为每一个Task都分配一个WaitHandle,那么释放Task将是一个好措施因为这样能提高性能。但是事实并非如此,现实中,为Task分配WaitHandle的情况是非常少出现的。.NET 4.0中,WaitHandle在以下几种情况会延迟初始化:访问 ((IAsyncResult)task).AsyncWaitHandle成员,或者调用Task的WaitAll()/WaitAny()方法(这两个方法在.NET4.0版本中,内部是基于Task的WaitHandle对象实现的)。这使得回答“是否应该释放Task”问题更加困难了,因为如果Task都使用了WaitAll()/WaitAny(),那么释放Task就是一个好选择。

1
2
3
4
5
6
7
public interface IAsyncResult
{
    object AsyncState { get; }
    WaitHandle AsyncWaitHandle { get; }
    bool CompletedSynchronously { get; }
    bool IsCompleted { get; }
}

         在.NET 4.0中,一个Task一旦被释放,它的大多数成员访问都会抛出ObjectDisposedExceptions异常。这使得完成的任务很难被安全的缓存,因为一个消费者释放Task后,另一个消费者无法再访问Task的一些重要成员,如ContinueWith()方法或Result属性。

         这里还有另外一个问题:Task是基础同步基元。如果Task被用于并行化,如在一个fork/join模式(”分支/合并”模式)中那么它就很容易知道什么时候完成它们和什么时候没有人再使用它们,比如:

1
2
3
4
5
6
var tasks = new Task[3];
tasks[0] = Compute1Async();
tasks[1] = Compute2Async();
tasks[2] = Compute3Async();
Task.WaitAll(tasks);
foreach(var task in tasks) task.Dispose();

         然而,当使用Task的延续任务时,就很难判断它什么时候完成它们和什么时候没有人再使用它们,比如:

1
2
3
4
5
Compute1Async().ContinueWith(t1 =>
{
    t1.Dispose();
    
});

示例成功的释放掉Compute1Async()返回的Task,但是它忽略了如何释放ContinueWith()返回的Task。当然,我们能使用同样的方法释放这个Task。

1
2
3
4
5
Compute1Async().ContinueWith(t1 =>
{
    t1.Dispose();
    
}).ContinueWith(t2 => t2.Dispose());

但是我们不能释放第二个ContinueWith()返回的Task。即使使用C#5.0中新的async/await异步方法也不能解决。例如:

1
2
3
string s1 = await Compute1Async();
string s2 = await Compute2Async(s1);
string s3 = await Compute3Async(s2);

如果想释放这些Task,我需要进行像下面这样的重写:

1
2
3
4
5
6
7
string s1 = null, s2 = null, s3 = null;
using(var t1 = Compute1Async())
    s1 = await t1;
using(var t2 = Compute2Async(s1))
    s2 = await t2;
using(var t3 = Compute3Async(s2))
    s3 = await t3;

 

解决方案

         由于像上面这样进行释放大多数Task显得很繁琐,所以在.NET4.5中已经对Task的Dispose()做过一些改进:

1. 我们使得你更少机会为Task创建WaitHandle对象。在.NET4.5中我们已经重写了Task的WaitAll()和WaitAny()以致这两个方法不再依赖与WaitHandle对象(这样WaitAll()、WaitAny()、Wait()就都基于自旋等待),避免在Task的内部实现中使用WaitHandle对象,并且提供async/await相关异步功能。因此,只有当你显示访问Task的IAsyncResult.AsyncWaitHandle成员才会为Task分配WaitHandle对象,但这种需求非常少见。这意味着除了这种非常少见的情况外,释放一个任务是不需要的。

2.      我们使得Task在释放后依然可用。你能使用Task的所有公开成员即使Task已经被释放,访问这些成员的表现就和释放Task之前一样。只有IAsyncResult.AsyncWaitHandle成员你不能使用,因为这是你释放Task时真真所释放的对象,当你尝试在释放Task后访问这个属性时依然会抛出ObjectDisposedException。此外,更进一步的说,现在我们推荐使用async/await异步方法以及基于任务的异步编程模式,降低对IAsyncResult的使用,即使你继续使用((IAsyncResult)task),调用其AsyncWaitHandle成员也是十分罕见的。

3.         Task.Dispose()方法在“.NET Metro风格应用程序”框架所引用的程序集中甚至并不存在(即此框架中Task没有实现IDisposable接口)。

 

指南

所以,这又让我们回到了简要的回答:“不是,不用释放你的Task。”通常很难找到一个合适的释放点,目前几乎没有一个理由需要去主动释放Task(因为调用((IAsyncResult)task).AsyncWaitHandle成员的需求是十分罕见的),并且在“.NET Metro风格应用程序”框架所引用的程序集中你甚至不能调用Task的Dispose()方法。 

                   

 

更多资源来源博文:关于Async与Await的FAQ

 

 

原文:http://blogs.msdn.com/b/pfxteam/archive/2012/03/25/10287435.aspx

作者:Stephen Toub - MSFT

 

 


作者:滴答的雨
出处:http://www.cnblogs.com/heyuquan/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/437290.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qt:QSound无法播放.wav声音的解决办法

从网上下载了音频素材,格式为.wav,用QSound播放,没声音。刚开始放在资源文件里,后来看到有的人说不能引用资源文件里的音频文件,事实证明纯属扯淡!改为播放本地文件系统内的音频文件,但是仍然无…

【转】UML基础: 第1部分 - 类图 (Class Diagram)

类图 类图是一个静态图。它代表了应用程序的静态视图。类图不仅用于可视化,描述和记录系统的不同方面,还用于构建软件应用程序的可执行代码。 类图描述了一个类的属性和操作,以及对系统施加的约束。类图被广泛用于面向对象系统的建模&#…

Qt QSS知识点记录

一、border-image的使用 具体使用方法参考css3的相关说明,这里主要记录一个使用技巧。 使用时发现按照css3指定的方法来设置边缘非拉伸区的宽度并没有效果。如 border-image: url(test.png) 10 10 10 10; 后来在网上搜索到一篇文章,提供了一个解决方…

int转换为cstring_PostgreSQL 隐式类型转换探秘

个人简介何小栋, 从事产品研发和架构设计工作,对Oracle、PostgreSQL有深入研究,ITPUB数据库版块资深版主。现就职于广州云图数据技术有限公司,系统架构师,博客:http://blog.itpub.net/6906/摘要本文通过与O…

【转】UML基础: 第 2 部分 - 对象图 (Object Diagram)

对象图是从类图派生的,因此对象图依赖于类图。 对象图表示类图的一个实例。类图和对象图的基本概念是相似的。对象图也表示系统的静态视图,但这个静态视图是系统在特定时刻的快照。 对象图用于呈现一组对象及其关系作为实例。 对象图的目的 图表的目…

Qt 界面设计笔记

1、今天遇到一个情形,在QScrollArea中设置一个QLabel的大小和QScrollArea一样大,设置完立即打印,大小的确是相同的。但是程序启动后,却显示出了滚动条,即QLabel比QScrollArea大。程序运行起来之后在事件响应函数中打印…

【转】1.C Task.CompletedTask和Task.Result小记

在任何返回Task的方法中,如果可以在不进行异步的情况下计算结果,则最好避免使用Task.Run。例如,一个简短的计算函数,或者测试中返回了一个预先计算过的结果,则无需使用Task.Run。 例如,定义了一个返回Task的…

外部依赖项很多未定义标识符_从日本编程书籍《我的第一本编程书》中译版看中文例程如何扬长避短——标识符(一)

日本作者平山尚在前言归结了本书的三点独特之处:从始至终只编写一个程序(俄罗斯方块游戏)使用专门的工具绝对面向首次接触程序的人群第一点,优势是一个项目主体贯穿全书,但同时很考验编排顺序,以及技术覆盖…

Qt: QTableView如何获取(行)选中、行切换信息

**情景:**做一个信息表格,需要多个Model切换,必须用QTableView,而不能用QTableWidget,因为后者不可以进行setModel()。 方案: QTableView和选择有关的的信号有: void activated(const QModelI…

动态网站的技术路线_3个好玩实用小网站!闲暇时间不妨打开看看

感谢你关注“最佳应用”每篇文章解决某行业或某人群的一个痛点第八十四期原创文章By:小佳昨天刷抖音听了一首很有魔性的歌曲,结果分享到社交平台,没想到被很多键盘侠喷了,留言全是批判“审美有毒”,这种垃圾歌曲能火就…

【转】1.DThread、ThreadPool、Task、Parallel的基本用法、区别以及弊端

多线程的操作在程序中也是比较常见的,比如开启一个线程执行一些比较耗时的操作(IO操作),而主线程继续执行当前操作,不会造成主线程阻塞。线程又分为前台线程和后台线程,区别是:整个程序必须要运行完前台线程才会退出&a…

Qt使用导出类报错:error C2491: “ZMapWidget::staticMetaObject”: 不允许 dllimport 静态数据成员 的定义

在使用一个继承自QObject带有Q_OBJECT宏的导出类时,编译报错:不允许 dllimport 静态数据成员 的定义。 原因是自动生成的moc文件带有静态函数,无法导出。 1、在Qt中的解决办法是不将导出宏定义成Q_DECL_IMPORT。 #if defined(ZMAP_LIBRARY…

【转】2.1(译)关于async与await的FAQ

传送门:异步编程系列目录…… 环境:VS2012(尽管System.Threading.Tasks在.net4.0就引入,在.net4.5中为其增加了更丰富的API及性能提升,另外关键字”async”和”await”是在C#5.0引入的。vs2010打 Visual Studio Async …

vue传值到后端_Vue.js快速入门就从这儿开始特别是后端程序员

自从前后端分离开始变成主流后,曾经的Jsp、FreeMarker、Velocity、Thymeleaf貌似慢慢被遗忘了,取而代之的是兴起的前端主流语言,比如Vue、React和AngularJS介绍VueVue其实是借鉴了 Angular,目前GitHubstar数最多,建议后…

Qt全局信号通信

应用场景分析 Qt开发中经常会遇到作用域跨度比较大的对象间通信的场景,如果直接使用信号槽通过对象指针直接连接,首先需要将对象指针互相暴露出来,其中可能涉及到各种复杂的传递过程,导致程序混乱。一种解决方案是建立全局的信号…

Qt创建浮动子窗口

想要实现子窗口在父窗口上方浮动显示,点击父窗口,子窗口不会被父窗口覆盖,有两种方法: 1、使用QDialog,使用show()显示窗口。 2、子类继承自QWidget,并设置窗口标志Qt::Tool。

unity3d collider自动调整大小_自动网格组合建模工具Unity游戏素材资源

分享最新的CG教程与素材资讯!人人素材RRCG-专业的CG艺术交流网站点击上方蓝字关注人人素材本游戏资料是自动网格组合建模工具Unity游戏素材资源,大小:735 KB ,格式:unitypackage,使用软件:unity…

【转】2.2[译]async/await中阻塞死锁

这篇博文主要是讲解在async/await中使用阻塞式代码导致死锁的问题,以及如何避免出现这种死锁。内容主要是从作者Stephen Cleary的两篇博文中翻译过来. 原文1:DontBlock on Async Code 原文2:why the AspNetSynchronizationContext was remove…

Java运用自身排序算法将数组或容器进行随机打乱。

基本思路&#xff1a;数组调用Arrays.sort(T[] a,Comparator<? super T> c),对Comparator进行重写。运用Random类 &#xff0c;实现对数字的随机排序。 对数字进行随机排序。代码如下&#xff1a; import java.util.Arrays; import java.util.Comparator; import java…

文本编码解释

一张图解释字符集 举例说明什么是编码&#xff1a; UTF-8编码 等长编码对于英文来说浪费空间&#xff0c;所以出现了变长编码UTF系列&#xff0c;如UTF8&#xff0c;UTF16&#xff0c;UTF32。 UTF8的编码对象是整个Unicode字符集&#xff0c;所以可以表示所有国家的语言而不会…