【转】1.A(译).NET4.X 并行任务中Task.Start()的FAQ

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

 

         近期有不少人向我咨询关于Task的Start()方法。比如:何时使用及何时不使用Start()、Start()又做了些什么……我想在这里回答一些问题试图澄清和平息任何关于Start()方法是什么以及做了什么的误解。

 

1.         问题:我什么时候能使用Task的Start()方法?

         只有Task处于TaskStatus.Created状态时才能使用实例方法Start()。并且,只有在使用Task的公共构造函数构造的Task实例才能处于TaskStatus.Created状态。

表示 Task 的生命周期中的当前阶段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public enum TaskStatus
{
    // 该任务已初始化,但尚未被计划。
    Created = 0,
    // 该任务正在等待 .NET Framework 基础结构在内部将其激活并进行计划。
    WaitingForActivation = 1,
    // 该任务已被计划执行,但尚未开始执行。
    WaitingToRun = 2,
 
    // 该任务正在运行,但尚未完成。
    Running = 3,
    // 该任务已完成执行,正在隐式等待附加的子任务完成。
    WaitingForChildrenToComplete = 4,
 
    // 已成功完成执行的任务。
    RanToCompletion = 5,
    // 该任务已通过对其自身的 CancellationToken 引发 OperationCanceledException 异常
    Canceled = 6,
    // 由于未处理异常的原因而完成的任务。
    Faulted = 7,
}

 

2.         问题:使用Task.Run()/Task.ContinueWith()/Task.Factory.StartNew()/TaskCompletionSource/异步方法(即使用async与await关键字的方法)……应该调用Start()方法吗?

不应该。不仅不应该,而且也不能,因为此时调用Start()会报异常。从问题1可知:Start()实例方法只适用于TaskStatus.Created状态的Task。由上面提到的方式创建的Task其状态不是TaskStatus.Created,而是如TaskStatus.WaitingForActivation、TaskStatus.Running或TaskStatus.RanToCompletion。

 

3.         问题:Start()方法实际做了什么?

Start()将任务排队到目标TaskScheduler(无参的Start()重载任务调度者为TaskScheduler.Current)。当你使用Task的构造函数创建一个Task实例时,它处于创建状态(TaskStatus.Created),它没有与任何调度器关联,也没有真真被执行。如果你永远不调用Start()方法,那么此任务永远不会排队也不会完成。为了让任务被执行,它需要在调度器上进行排队,以便调度器在合适的时刻执行它。在Task上调用Start()方法将改变任务内部的一些数据(eg:状态从Created改变为WaitingToRun)并且将任务通过TaskScheduler实例的QueueTask()方法排队到目标调度器。此时,此任务未来的执行掌握在调度器手中,最终会通过TaskScheduler的TryExecuteTask实例方法执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
     // 表示一个处理将任务排队到线程中的底层工作的对象。
     public abstract class TaskScheduler
     {
          protected TaskScheduler();
  
          // 获取与当前正在执行的任务关联的 TaskScheduler。
          public static TaskScheduler Current { get; }
          // 获取由 .NET Framework 提供的默认 TaskScheduler 实例。
          public static TaskScheduler Default { get; }
          // 创建一个与当前 SynchronizationContext 关联的 TaskScheduler。
          public static TaskScheduler FromCurrentSynchronizationContext();
 
          // 当出错的 Task 的未观察到的异常将要触发异常升级策略时发生,默认情况下,这将终止进程。
          public static event EventHandler<UnobservedTaskExceptionEventArgs> UnobservedTaskException;
 
          // 获取此 TaskScheduler 实例的唯一 ID。
          public int Id { get; }
          // 指示此 TaskScheduler 能够支持的最大并发级别,默认计划程序返回 System.Int32.MaxValue。
          public virtual int MaximumConcurrencyLevel { get; }
 
          // 仅对于调试器支持,生成当前排队到计划程序中等待执行的 Task 实例的枚举。
          protected abstract IEnumerable<Task> GetScheduledTasks();
          // 将 Task 排队到计划程序中。
          protected internal abstract void QueueTask(Task task);
          // 尝试将以前排队到此计划程序中的 Task 取消排队。
          protected internal virtual bool TryDequeue(Task task);
          // 尝试在此计划程序上执行提供的 Task。执行失败的常见原因是,
          // 该任务先前已经执行或者位于正在由另一个线程执行的进程中。
          protected bool TryExecuteTask(Task task);
          // 确定提供的 Task是否可以在此调用中同步执行,如果可以,将执行该任务。
          //   taskWasPreviouslyQueued:
          //     一个布尔值,该值指示任务之前是否已排队。如果此参数为 True,则该任务以前可能已排队(已计划);
          //     如果为 False,则已知该任务尚未排队,此时将执行此调用,以便以内联方式执行该任务,而不用将其排队。
          // 返回结果:
          //     一个布尔值,该值指示是否已以内联方式执行该任务。
          protected abstract bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued);
}

 

4.         问题:我能在同一个Task上调用多次Start()方法吗?

不行,Task只能离开创建状态(TaskStatus.Created)一次,而Start()方法使Task离开创建状态。因此,Start()方法只能使用一次。任何企图在不是创建状态的Task上调用Start()都将导致一个异常。Start()方法采用同步方式运行以确保任务对象保持一致的状态,即使是同时调用多次Start(),也只可能有一个调用会成功。

 

5.         问题:使用Task.Start()与使用Task.Factory.StartNew()有何不同?

Task.Factory.StartNew()可快速创建一个Task并且开启任务。代码如下:

1
var t = Task.Factory.StartNew(someDelegate);

这等效于:

1
2
var t = new Task(someDelegate);
t.Start();

表现方面,前者更高效。就像在问题3中提到的,Start()采用同步方式运行以确保任务对象保持一致的状态即使是同时调用多次Start(),也可能只有一个调用会成功。相比之下,StartNew()知道没有其他代码能同时启动任务,因为在StartNew()返回之前它不会将创建的Task引用给任何人,所以StartNew()不需要采用同步方式执行。

 

6.         问题:我听说Task.Result属性也能开启任务,真的吗?

不能,只有两种方式可能使Task离开其创建状态

1)         将一个CancellationToken传递给Task的构造函数,并且这个token已经或稍后请求取消。如果Task任然处于Created/WaitingForActivation/WaitingToRun,当token取消时,Task将改变为TaskStatus.Canceled状态。

2)         在Task上调用Start()。

因此, Result属性不能开启任务。如果对处于创建状态的Task实例上调用Wait()方法或Result属性,这个调用将被阻塞,需要等待开启任务,这样它就可以排队到调度器,调度程序最终会执行它,然后完成任务。被阻塞的调用就被唤醒。

         你可能会认为不是这样的。Result能开启任务,但是这只适用于“内联”任务的执行。如果一个任务已经排队到TaskScheduler,但是这个任务可能任然保持在调取器的任务队列中。当你对被排队的任务请求Result属性时,运行时将尝试内联任务的执行而不是纯粹的阻塞和等待调度器在未来某个时刻使用其他线程来完成任务执行。因此,调用Result属性可能终止于TaskScheduler的TryExecuteTaskInline()方法的调用,并且如何处理请求由TaskScheduler决定。

 

7.         问题:我应该提供返回未启动任务的公共APIs吗?

更恰当的问题是“我应该提供处于创建状态任务的公共APIs吗”,答案是“不能”。

         基本原因是:当你正常调用同步方法,该方法将很快被调用执行。对于返回Task的方法,你可以把Task看做是异步方法完成的结果。但是这并不能改变需要调用Start()方法开始相关操作的事实。因此,返回一个处于创建状态的Task的异步方法是很奇怪的,只是想代表一个没有开始的操作?

         因此,如果你有一个返回Task的公共方法,并且Task是使用构造函数创建的,请确保你在返回Task之前开启任务。否则,很可能在APIs使用方导致死锁或类似的问题,因为使用方期待调用完成时Task也最终完成,但如果返回一个尚未启动的任务,它将永远不会完成。有些框架允许你参数化方法或委托,返回Task甚至验证返回任务的状态,如果Task还是创建状态就为其抛出异常。

 

8.         问题:我应该使用Task的构造函数 + Task的Start()实例方法吗?

在大多数情况下,你最好使用一些其他机制。比如,如果你只是想计划一个任务来运行你提供的委托,你最好使用Task.Run()静态方法或Task.Factory的StartNew()实例方法,而不是使用Task的构造函数创建一个任务再调用Start()开启它,这不仅仅减少了代码量,并且更加高效(见问题5回答),另外还可以减少犯错的可能,比如忘记开启任务。

当然,在一些情况下使用Task的构造函数+Start()更加有意义。比如,需要根据某些原因来选择传递而来的Task,然后再使用Start()方法来实际排队任务。

另外,一个更明显的示例是,如果你想获得任务本身的引用,可能使用了如下代码:

1
2
Task theTask = null;
theTask = Task.Run(() => Console.WriteLine(“My ID is {0}.”, theTask.Id));

         有问题,存在竞争?在Task.Run()方法内部,会创建一个新Task对象并且将其排队到线程池调度器中。如果线程池比较空闲,那么会立即分配一个辅助线程开始执行任务。新创建的任务最后会存储在theTask变量中,辅助线程和调用Task.Run()的线程会发生竞争的访问此变量。我们能解决这种竞争通过分离构造函数与TaskScheduler:

1
2
3
Task theTask = null;
theTask = new Task(() =>Console.WriteLine(“My ID is {0}.”, theTask.Id));
theTask.Start(TaskScheduler.Default);

         现在我们已经确保Task实例将在线程池执行任务之前被存储到theTask变量。因为线程池在Task对象调用Start()排队任务之前无法获得Task对象的引用,并且在这个时候,变量theTask已经设置为Task的引用,与后面线程池访问theTask.Id不会存在竞争问题。

 

推荐并行任务相关资源:《关于Async与Await的FAQ》

 

================================================================================================

 

园友提醒:(.NET4.5对.NET4.0的并行任务进行过改进,然而我正式学习并行任务的时候已经是.NET4.5,所以对于新改进的API没有进行整理了,这边有园友提醒,做下记录,方便大家。)

      @zhangweiwen(Task.Run()是.net4.5新提供的API)

 

================================================================================================

 

         Ok,看完此文,相信你对并行任务中关于任务开启又有更深入的理解了,(*^_^*),喜欢还请多多推荐。

 

原文:http://blogs.msdn.com/b/pfxteam/archive/2012/01/14/10256832.aspx

作者:Stephen Toub

 

 


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

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

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

相关文章

python列表添加数字_Python-识别列表中的连续数字组

小编典典 编辑2&#xff1a;回答OP新要求 ranges [] for key, group in groupby(enumerate(data), lambda (index, item): index - item): group map(itemgetter(1), group) if len(group) > 1: ranges.append(xrange(group[0], group[-1])) else: ranges.append(group[0]…

QML项目笔记

引用资源文件内的资源时&#xff0c;一律加上qrc前缀&#xff0c;如&#xff1a;qrc:/img/avatar.png&#xff0c;否则无法加载。制作聊天气泡的方法&#xff1a;使用BorderImage元素。BorderImage源于CSS3新增的十分强大的border-image属性。可以实现安卓中的 点9图 效果。慎用…

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

传送门&#xff1a;异步编程系列目录…… 摘要&#xff1a;本博文解释在.NET 4.X中的Task使用完后为什么不应该调用Dispose()。并且说明.NET4.5对.NET4.0的Task对象进行的部分改进&#xff1a;减轻Task对WaitHandle对象的依赖&#xff0c;并且增强在释放了Task后对其成员的可访…

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

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

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

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

Qt QSS知识点记录

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

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

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

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

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

Qt 界面设计笔记

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Qt全局信号通信

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

Qt创建浮动子窗口

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

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

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