任务(Task)是一个管理并行工作单元的轻量级对象。它通过使用CLR的线程池来避免启动专用线程,可以更有效率的利用线程池。System.Threading.Tasks 命名空间下任务相关类一览:

作用
Task 管理工作单元
Task<TResult> 管理带返回值的工作单元
TaskFactory 创建任务
TaskFactory<TResult> 创建任务或者有相同返回值的延续任务
TaskScheduler 管理任务调度
TaskCompletionSource 手动控制任务工作流

任务用来并行地执行工作,充分地利用多核:事实上,Parallel和PLINQ内部就是建立在任务并行的结构上。

任务提供了一系列强大的特性来管理工作单元,包括:

  • 协调任务调度
  • 建立一个任务从另一个任务中启动的父子关系
  • 实现合作取消(cooperative cancellation)模式
  • 无信号的任务等待
  • 附加延续任务(continuation)
  • 基于多个祖先任务调度一个延续任务
  • 传递异常到父任务、延续任务或任务消费者

同时任务实现了一个本地工作队列,它允许你高效地创建快速执行的子任务而不用遭受在单个工作队列时的竞争花费。任务并行库让你用最小的花费来创建成百上千的任务,但是如果你想创建上百万个任务,就必须分割这些任务到更大的工作单元,以保持效率。

创建与启动任务

有两种方法可以创建任务,一种是通过TaskFactory的StartNew()方法创建并启动任务;另一种是调用Task构造函数创建,然后手动启动任务。需要注意的是,任务启动后并不会立即执行,它是由任务调度器(TaskScheduler)来管理的。

  • TaskFactory的StartNew()方法创建任务的示例如下:
    //没有返回值
    Task.Factory.StartNew(() => Console.WriteLine("Task Created!"));
    //有返回值
    var task = Task.Factory.StartNew<string>(() => "Task Created!");
    Console.WriteLine(task.Result);
  • 调用Start方法手动启动的示例如下:
    var task = new Task<string>(() => "Task Created!");
    task.Start();//异步执行
    Console.WriteLine(task.Result);
  • 调用RunSynchronously方法手动启动的示例如下:
    var task = new Task<string>(() => "Task Created!");
    task.RunSynchronously();//同步执行
    Console.WriteLine(task.Result);

也可以在创建任务时指定一个任务状态参数,可以通过任务的AsyncState属性来访问该参数。示例:

var task = Task.Factory.StartNew(state => "hello " + state, "Mike");
Console.WriteLine(task.AsyncState);
Console.WriteLine(task.Result);

你还可以指定一个任务创建选项(TaskCreationOptions) ,这个枚举类型有以下枚举值:None,LongRunning,PreferFairness,AttachedToParent。下面解释各个枚举值的作用。

  • LongRunning:顾名思义就是长时间运行的任务,此选项建议任务调度器分配一个专用的线程给任务。这样做的原因是:长时间运行的任务可能会阻塞任务队列,导致那些短小的任务一直得不到执行。LongRunning也适合那些阻塞的任务。
  • PreferFairness:公平第一,此选项建议任务调度器尽量按照任务的启动时间来调度任务。但是它通常可能不这样做,因为它使用本地工作偷取队列(local work-stealing queues)优化任务调度。这个优化对那些非常小的任务很有用。
  • AttachToParent:附加到父任务,此选项用来创建子任务。创建子任务示例:
    第一种方式:
    var parent = Task.Factory.StartNew(() =>
    {
        var nonChildTask = Task.Factory.StartNew(
            () => Console.WriteLine("I'm not a child task.")
        );
        var childTask = Task.Factory.StartNew(
            () => Console.WriteLine("I'm a child task."),
        TaskCreationOptions.AttachedToParent);
    });
    第二种方式:
    Task parent=new Task(()=>
    {
        DoStep1();
    });
    Task task2 = parent.ContinueWith ((PrevTask) =>
    {
        DoStep2();
    });
    parent.Start();

任务等待

任务可以通过Wait()成员方法或Result属性来等待任务完成。

当调用Result属性时,将会执行下列操作:

  1. 如果任务已结束,返回任务结果
  2. 如果任务已开始,等待任务结束
  3. 如果任务尚未开始执行,则在当前线程执行任务

Task.WaitAny()静态方法等待任何一个任务完成。示例:

var tasks = ];
; i < tasks.Length; i++)
{
    int taskIndex = i;
    tasks[i] = Task.Factory.StartNew(() =>
    {
        int seed=Guid.NewGuid().GetHashCode();
        , );
        Thread.Sleep(waitTime);
        Console.WriteLine("Task{0} Finished", taskIndex);
    });
}
Task.WaitAny(tasks);
Console.WriteLine(];
; i < tasks.Length; i++)
{
    int taskIndex = i;
    tasks[i] = Task.Factory.StartNew(() =>
    {
         , );
         Thread.Sleep(waitTime);
         Console.WriteLine("Task{0} Finished", taskIndex);
    });
}
Task.WaitAll(tasks);
Console.WriteLine("所有任务完成");

异常处理

默认情况下任务未处理的异常会终止应用程序。需要指出的是任务中未处理的异常不会立即导致应用程序终止,异常要延迟到垃圾回收器回收任务并调用Finalize方法时才会终止程序。如果读取了任务的Exception属性,这个操作将阻止随后的应用程序终止。
当等待任务完成时,所有未处理的异常会传递到调用方。
Wait()方法超时的异常也必须处理,否则导致应用程序终止。
子任务中未处理的异常会冒泡传递到父任务;嵌套任务中的非子任务的异常不会传递到这个任务的上一层任务,需要单独处理,否则将导致应用程序终止。

var task = Task.Factory.StartNew(() =>
{
    Task.Factory.StartNew(() => { throw null; }, TaskCreationOptions.AttachedToParent);
    Task.Factory.StartNew(() => { throw null; }, TaskCreationOptions.AttachedToParent);
    Task.Factory.StartNew(() => { throw null; }, TaskCreationOptions.AttachedToParent);
});
task.Wait();

TaskScheduler.UnobservedTaskException静态事件提供了最后一种手段处理所有未处理异常。通过处理这个事件,就不用终止应用程序,而用你自己的异常处理逻辑替代它。

取消任务

当创建任务时,可以传入一个取消令牌(CancelationToken)参数,这样就可以安全的取消任务。示例:

var source = new CancellationTokenSource();
var token = source.Token;
var task = Task.Factory.StartNew(() =>
{
    Console.WriteLine("Task starting...");
    while (true)
    {
        token.ThrowIfCancellationRequested();
        Console.WriteLine("I'm alive. {0}",DateTime.Now);
        Thread.Sleep();
    }
},token);

Task.Factory.StartNew(() =>
{
    Thread.Sleep();
    source.Cancel();
});

try
{
    task.Wait();
    Console.WriteLine("Task stopped.");
}
catch (AggregateException e)
{
    if (e.InnerException is OperationCanceledException)
    {
        Console.WriteLine("Task canceled.");
    }
    else
    {
        Console.WriteLine("errors.");
    }
}

通过调用CancellationTokenSource的Cancel()方法取消任务,这个并不会立即终止任务,一直延迟到任务下次检测是否取消时才通过抛出OperationCanceledException终止任务。

如果想通过直接抛出OperationCanceledException异常的方式取消任务,则需要在任务中传入CancelationToken参数,否则就不能将任务的状态为TaskStatus.Canceled并触发OnlyOnCanceled延续任务。

此外,取消令牌也可以传递到Wait和CancelAndWait方法中来取消等待。

.Net4.0 任务(Task)的更多相关文章

  1. .Net4.0 任务(Task)[转]

    .Net4.0 任务(Task) 任务(Task)是一个管理并行工作单元的轻量级对象.它通过使用CLR的线程池来避免启动专用线程,可以更有效率的利用线程池.System.Threading.Tasks ...

  2. (译).NET4.X并行任务Task需要释放吗?

    摘要:本博文解释在.NET 4.X中的Task使用完后为什么不应该调用Dispose().并且说明.NET4.5对.NET4.0的Task对象进行的部分改进:减轻Task对WaitHandle对象的依 ...

  3. .Net4.0如何实现.NET4.5中的Task.Run及Task.Delay方法

    前言 .NET4.0下是没有Task.Run及Task.Delay方法的,而.NET4.5已经实现,对于还在使用.NET4.0的同学来说,如何在.NET4.0下实现这两个方法呢? 在.NET4.0下, ...

  4. CefSharp v62修改,支持.net4.0

    吐槽一下,博客园久了没有上,账号没了,重新申请一个. cesharp v62版本,内核采用最新的Cef 62,支持最新的Grid布局. 由于官方的cefsharp 采用.net4.5.2开发.怎么办怎 ...

  5. 基于.Net4.0实现 ToastNotification

    基于.Net4.0实现 ToastNotification Windows更新之路的特色之一就是消息提示由气泡变成了通知窗口,效果简直不要太好.最近公司有这方面的需求,需要在xp,win7系统上给出提 ...

  6. “RazorEngine.Templating.TemplateCompilationException”类型的异常在 RazorEngine.NET4.0.dll 中发生,但未在用户代码中进行处理

    错误信息: "RazorEngine.Templating.TemplateCompilationException"类型的异常在 RazorEngine.NET4.0.dll 中 ...

  7. asp.net 项目Net4.0 在IE10、 IE 11 下出现 “__doPostBack”未定义 的解决办法

    我的项目中,服务器端是Windows Server2008 64位,.net版本是4.0,也遇到了树形结构控件.DropDownList控件等不能调用服务器端代码.最后发现js报错. 错误信息:“__ ...

  8. IIS6.0添加上.net4.0后,以前的.net系统出现“服务器应用程序不可用”的错误提示解决办法

    把VS2010开发的网站.net4.0部署到Windows Server 2003的服务器上去, Windows Server 2003操作系统自带的为IIS 6.0,IIS 6.0一般只支持.NET ...

  9. 关于把.net 2.0的项目升级到.net4.0遇到的一些问题

    进入公司实习的的第一个项目又是是一个升级项目.这次升级的是一个c/s架构的项目. 大致介绍一下这个项目的结构客户端采用winform+devexpress商业控件开发的,数据库是用的oracle数据库 ...

随机推荐

  1. CentOS7 Nvidia Docker环境

    最近在搞tensorflow的一些东西,话说这东西是真的皮,搞不懂.但是环境还是磕磕碰碰的搭起来了 其实本来是没想到用docker的,但是就一台配置较好电的服务器,还要运行公司的其他环境,vmware ...

  2. POJ [P2289] Jamie's Contact Groups

    二分+二分图多重匹配 辣鸡ACM式读入 对于这种奇葩的读入方法,还是老老实实的用scanf吧 #include <iostream> #include <cstdio> #in ...

  3. 洛谷 [P1801] 黑匣子

    这道题是一道splay裸题,然而身为蒟蒻的我并不会,所以这道题我维护的是一个大根堆与一个小根堆结合起来的类似沙漏的结构. 本题难点在于询问的不是最大最小值,而是第K小值,所以我们想到了维护这样两个堆, ...

  4. Getting the pixel coordinates of text or ticks in matplotlib

    The exact pixel coordinates of title, labels, legends or ticks are important information for the tra ...

  5. 关于node的基础理论,书上看来的

    最近看了一本书,说了一些Node.js的东西,现在来记录一下,让自己记得更牢靠一点. 在书上,是这样介绍的:Node.js模型是源于Ruby的Event Machine 和 Python的Twiste ...

  6. ACE在windows下的编译及配置(VS2010)

    ACE在windows下的编译及配置(VS2010) 分类:             -[小西南]-              2013-08-06 16:17     2354人阅读     评论( ...

  7. memcached安装与使用详解

    一.memcache的简介 memcache是高速,分布式的内存缓存服务器 php的缓存方式一般可以使用memcache技术和redis技术,其中各有优劣,因不同的情况而选择较为适合的缓存技术,其中m ...

  8. location对象浅探

  9. CentOS6.8配置GO语言开发环境

    Go语言是谷歌2009发布的第二款开源编程语言,Go语言专门针对多处理器系统应用程序的编程进行了优化,使用Go编译的程序可以媲美C或C++代码的速度,而且更加安全.支持并行进程. 鉴于原来越多的开源项 ...

  10. XAMPP禁止目录浏览的方法

    XAMPP是目前比较流行Web服务器套件,集成了Apache.MySQL.PHP.PERL.FTP等各种软件包.但是细心的人可以发现,XAMPP安装完成后,默认是可以目录浏览的,这有些不安全.如果需要 ...