任务(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. spring boot + vue + element-ui全栈开发入门——前端列表页面开发

     一.页面 1.布局 假设,我们要开发一个会员列表的页面. 首先,添加vue页面文件“src\pages\Member.vue” 参照文档http://element.eleme.io/#/zh-CN ...

  2. 好用的开源库(二)——uCrop 图片裁剪

    最近想要实现图片裁剪的功能,在Github上找到了这个uCrop,star的人挺多的,便是决定入坑,结果长达一个小时的看资料+摸索,终于是在项目中实现了图片裁剪的功能,今天便是来介绍一下uCrop的使 ...

  3. bzoj 2655: calc [容斥原理 伯努利数]

    2655: calc 题意:长n的序列,每个数\(a_i \in [1,A]\),求所有满足\(a_i\)互不相同的序列的\(\prod_i a_i\)的和 clj的题 一下子想到容斥,一开始从普通容 ...

  4. BZOJ 2986: Non-Squarefree Numbers [容斥原理 二分]

    题意:求第$n \le 10^{10}$个不是无平方因子数 二分答案, 容斥一下,0个质数的平方因子-1个..... 枚举$\sqrt$的平方因子乘上莫比乌斯函数,最后求出无平方因子数的个数取补集 # ...

  5. POJ 1625 Censored! [AC自动机 高精度]

    Censored! Time Limit: 5000MS   Memory Limit: 10000K Total Submissions: 9793   Accepted: 2686 Descrip ...

  6. java Mac自动化-java和ant环境搭建

    本文旨在帮助读者介绍,如果一个测试工程师拿到了mac本,该如何在本地搭建java和ant环境 其实在几年前,我们还大多使用的是windows本,而且我们也会比较善于使用windows笔记本,但自从ma ...

  7. 定时跳转的两种方式(html + javaweb)

    html方式 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <ti ...

  8. sql的升阶

    前言:基本数据库操作根本无法满足实际的需要,需要引入更多的操作. 触发器-隐式的,主动的,更新数据表中的信息.带有inserted和deleted两个临时表,代表新操作和旧操作. 它是一种特殊的存储过 ...

  9. 自动创建字符设备,不需mknod

    自动创建设备文件 1.自动创建设备文件的流程 字符设备驱动模块 -->创建一个设备驱动class--->创建属于class的device--->调用mdev工具(自动完成)--> ...

  10. 超级简便的容器化部署工具(使用 ASP.NET Core 演示)

    Docker 改变了我们部署网站的方式,从原先的手动编译打包上传,到现在的构建镜像然后推送部署,让我们在配置环境上所花费的时间大大减少了.不仅如此,通过一系列相关的工具配合,可以很轻松的实现 CI.C ...