简介

  .NET 4包含新名称空间System.Threading.Tasks,它 包含的类抽象出了线程功能。 在后台使用ThreadPool。 任务表示应完成的某个单元的工作。 这个单元的工作可以在单独的线程中运行,也可以以同步方式启动一个任务,这需要等待主调线程。 使用任务不仅可以获得一个抽象层,还可以对底层线程进行很多控制。 
  在安排需要完成的工作时,任务提供了非常大的灵活性。 例如,可 以定义连续的工 作—— 在一个任务完成后该执行什么工作。 这可以区分任务成功与否。 另外,还可以在层次结构中安排任务。例如,父任务可以创建新的子任务。 这可以创建一种依赖关系,这样,取消父任务,也会取消其子任务。

启动任务

  要启动任务,可 以使用 TaskFactory类 或 Task类 的构造函数和 Start()方法。Task类的构造函数在创建任务上提供的灵活性较大。 
  在启动任务时,会创建Task类 的一个实例,利用Action或Action<object>委托不带参数或带一个object参数 ,可以指定应运行的代码,这类似于Thread类 。下面定义了一个无参数的方法。 在实现代码中,把任务的ID写入控制台中:

static void TaskMethod()
{
Console.WriteLine("running in a task");
Console.WriteLine("Task id: {0}",Task.CurrentId);
}

  在上面的代码中,可 以看到启动新任务的不同方式。第一种方式 使用实例化TaskFactory类 ,在其中把 TaskMedlod()方 法传递给StartNew()方法,就会立即启动任务。 第二种方式使用 Task类的构造函数。 实例化 Task对象时,任务不会立即运行,而是指定 Created状态。接着调用 Task类的Start()方法,来启动任务。 使用Task类 时,除了调用 Start()方法,还可以调用RunSynchronously()方法。这样,任务也会启动,但在调用者的当前线程中它正在运行,调用者需要一直等待到该任务结束。 默认情况下,任务是异步运行的。

//using task factory
TaskFactory tf = new TaskFactory();
Task t1 = tf.StartNew(TaskMethod); //using the task factory via a task
Task t2 = Task.TaskFactory.StartNew(TaskMethod); //using Task constructor
Task t3 = new Task(TaskMethod);
t3.Start();

  使用Task类的构造函数和TaskFactory类的StartNew()方法时,都可以传递TaskCreationOptions枚举中的值。设置LongRunning选项,可以通知任务调度器,该任务需要较长时间执行,这样调度器更可能使用新线程。如果该任务应关联到父任务上,而父任务取消了,则该任务也应取消,此时应设置 AuachToParent选项。PreferFairness的值表示,调度器应提取出已在等待的第一个任务。 如果一个任务在另一个任务内部创建,这就不是默认情况 。如果任务使用子任务创建了其他工作,子任务就优先于其他任务。 它们不会排在线程池队列中的最后。 如果这些任务应以公平的方式与所有其他任务一起处理,就设置该选项为PreferFairness。 
  

Task t4 = new Task(TaskMethod, TaskCreationOptions.PreferFairness);
t4.Start();

连续任务

   通过任务,可 以指定在任务完成后,应开始运行另一个特定任务,例如,一个使用前一个任务的结果的新任务,如 果前一个任务失败了,这个任务就应执行一些清理工作。 
  任务处理程序或者不带参数或者带一个对象参数,而连续处理程序有一个 Task类 型的参数,这里可以访问起始任务的相关信息: 
  

static void DoOnFirst()
{
Console.WriteLine("doing some task {0}",Task.CurrentId);
Thread.Sleep();
} static void DoOnSecond(Task t)
{
Console.WriteLine("task {0} finished", t.Id);
Console.WriteLine("this task id {0}", Task.CurrentId);
Console.WriteLine("do some cleanup");
Thread.Sleep();
}

   连续任务通过在任务上调用ContinueWith()方法来定义。也可以使用TaskFactory类来定义。t1.ContinueWith(DoOnSecond)方 法表示,调用DoOnSecond()方法的新任务应在任务t1结束时立即启动。在一个任务结束时,可以启动多个任务,连续任务也可以有另一个连续任务,如下面的例子所示:

Task t1 = new Task(DoOnFirst);
Task t2 = t1.ContinueWith(DoOnSecond);
Task t3 = t1.ContinueWith(DoOnSecond);
Task t4 = t2.ContinueWith(DoOnSecond);

   无论前一个任务是如何结束的,前 面的连续任务总是在前一个任务结束时启动。 使用TaskContinuationOptions枚举中的值,可以指定,连续任务只有在起始任务(或失败)结束时启动。一些可能的值是OnlyOnFaulted、 NotOnFaulted、 OnlyOnCanceled、 NotOnCanceled和OnlyOnRanToCompletion。

Task t5 = t1.ContinueWith(DoOnError, TaskContinuationOptions.OnlyOnFaulted);

任务层次结构

  利用任务连续性,可 以在一个任务结束后启动另一个任务。 任务也可以构成一个层次结构。 一个任务启动一个新任务时,就启动了一个父/子层次结构。 
  下面的代码段在父任务内部新建一个任务。 创建子任务的代码与创建父任务的代码相同,唯一的区别是这个任务从另一个任务内部创建。 
 

  • static void ParentAndChild()
    {
    var parent = new Task(ParentTask);
    parent.Start();
    Thread.Sleep();
    Console.WriteLine(parent.Status);
    Thread.Sleep();
    Console.WriteLine(parent.Status);
    } static void ParentTask()
    {
    Console.WriteLine("task id {0}", Task.CurrentId);
    var child = new Task(ChildTask);
    child.Start();
    Thread.Sleep();
    Console.WriteLine("parent started child");
    } static void ChildTask()
    {
    Console.WriteLine("child");
    Thread.Sleep();
    Console.WriteLine("child finished");
    }

  如果父任务在子任务之前结束,父任务的状态就显示为WaitingForChildrenToComplete。 只要子任务也结束时,父任务的状态就变成RanToCompletion。 当然,如 果父任务用TaskCreationOptions枚举中的 DetachedFromParent创建子任务时,这就无效。

任务的结果

  任务结束时,它可以把一些有用的状态信息写到共享对象中。这个共享对象必须是线程安全的。另一个选项是使用返回某个结果的任务。使用 Task类 的泛型版本,就可以定义返回某个结果的任务的返回类型。 
  为了返回某个结果任务调用的方法可以声明为带任意返回类型。示例方法TaskWithResult()利用一个元组返回两个int值。 该方法的输入可以是void或object类型,如下所示:

static Tuple<int, int> TaskWithResult(object division)
{
Tuple<int, int> div =(Tuple<int, int>)division;
int result = div.Item1/div.Item2;
int reminder = div.Item1%div.Item2;
Console.WriteLine("task creates a result..."); return Tuple.Create<int, int>(result, reminder);
}

  定义一个调用 StartWithResult()方法的任务时,要使用泛型类Task<Tresult>。 泛型参数定义了返回类型。通过构造函数,把这个方法传递给Func委 托,第二个参数定义了输入值。 因为这个任务在object参数中需要两个输入值,所以还创建了一个元组。 接着启动该任务。 Task实例t1的Result属性被禁用,并一直等到该任务完成。任务完成后,Result属性包含任务的结果。 
 

var t1 = new Task<Tuple<int, int>>(TaskWithResult, Tuple.Create<int, int>(, ));
t1.Start();
Console.WriteLine(t1.Result);
t1.Wait();
Console.WriteLine("result from task: {0} {1}",t1.Result.Item1, t1.Result.Item2);

  备注:上例中,Task<Tresult>构造函数调用了TaskFactory.StartNew 方法的 (Func<Object, TResult>, Object)重载。 
  function 
    类型:System.Func<Object, TResult> 
    一个函数委托,可返回能够通过任务获得的将来结果。

  state 
    类型:System.Object 
    包含 function 委托所用数据的对象。

  因此我们可以知道,为什么在实例化t1的时候,为什么要创建一个新的Tuple对象了。

转载来源:http://blog.csdn.net/honantic/article/details/46790707

C#实现多线程的方式:Task——任务的更多相关文章

  1. java中创建多线程的方式

    在java中比较常用的有三种创建多线程的方式. 方式一:继承Thread类,要重写run方法. 在MyThread类 public class MyThread extends Thread { @O ...

  2. c#中@标志的作用 C#通过序列化实现深表复制 细说并发编程-TPL 大数据量下DataTable To List效率对比 【转载】C#工具类:实现文件操作File的工具类 异步多线程 Async .net 多线程 Thread ThreadPool Task .Net 反射学习

    c#中@标志的作用   参考微软官方文档-特殊字符@,地址 https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/toke ...

  3. 转载 .Net多线程编程—任务Task https://www.cnblogs.com/hdwgxz/p/6258014.html

    .Net多线程编程—任务Task   1 System.Threading.Tasks.Task简介 一个Task表示一个异步操作,Task的创建和执行是独立的. 只读属性: 返回值 名称 说明 ob ...

  4. 异步多线程 Thread ThreadPool Task

    一.线程 Thread ThreadPool 线程是Windows任务调度的最小单位,线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针.程序计数器等),但代码区是共享的,即不同的线程可以 ...

  5. C# 多线程六之Task(任务)三之任务工厂

    1.知识回顾,简要概述 前面两篇关于Task的随笔,C# 多线程五之Task(任务)一 和 C# 多线程六之Task(任务)二,介绍了关于Task的一些基本的用法,以及一些使用的要点,如果都看懂了,本 ...

  6. 多线程实现方式---实现Runnable接口

    多线程实现方式---实现Runnable接口 一个类如果需要具备多线程的能力,也可以通过实现java.lang.Runnable接口进行实现.按照Java语言的语法,一个类可以实现任意多个接口,所以该 ...

  7. 多线程事儿(task)之 一(转载)

    此文转载作为记录,转载地址https://www.cnblogs.com/xiaoXuZhi/p/XYH_tsak_one.html 多线程,一个多么熟悉的词汇,作为一名程序员,我相信无论是从事什么开 ...

  8. 重新想象 Windows 8 Store Apps (43) - 多线程之任务: Task 基础, 多任务并行执行, 并行运算(Parallel)

    [源码下载] 重新想象 Windows 8 Store Apps (43) - 多线程之任务: Task 基础, 多任务并行执行, 并行运算(Parallel) 作者:webabcd 介绍重新想象 W ...

  9. C#多线程实现方法——Task/Task.Factary

    原文:C#多线程实现方法--Task/Task.Factary Task 使用 Task以及Task.Factory都是在.Net 4引用的.Task跟Thread很类似,通过下面例子可以看到. st ...

  10. .net 多线程 Thread ThreadPool Task

    先准备一个耗时方法 /// <summary>/// 耗时方法/// </summary>/// <param name="name">< ...

随机推荐

  1. 8、Spring Boot 2.x 服务器部署

    1.8 服务器部署 完整源码: Spring-Boot-Demos 1.8.1 jar包提取出来maven打包(避免每次重复打相同的jar包),pom.xml配置如下: <build> & ...

  2. 解压 .tar.xz 格式的压缩文件

    第一种方法: xz -d mysql-8.0.16-linux-glibc2.12-x86_64.tar.xz tar -xvf mysql-8.0.16-linux-glibc2.12-x86_64 ...

  3. 长脖子鹿放置【洛谷P5030】二分图最大独立集变形题

    题目背景 众周所知,在西洋棋中,我们有城堡.骑士.皇后.主教和长脖子鹿. 题目描述 如图所示,西洋棋的“长脖子鹿”,类似于中国象棋的马,但按照“目”字攻击,且没有中国象棋“别马腿”的规则.(因为长脖子 ...

  4. php安装扩展的地址

    1 查看扩展 phpinfo  or extention_loads  or php -m 下载扩展地址 http://pecl.php.net     or http://windows.php.n ...

  5. C语言学习笔记11- 文件I/O

    C语言学习笔记11- 文件I/O  ..待编辑 文件操作(CRT.C++.WIN API.MFC)

  6. C语言学习笔记2-程序基础和变量

    本系列文章由jadeshu编写,转载请注明出处.http://blog.csdn.net/jadeshu/article/details/50751977 作者:jadeshu   邮箱: jades ...

  7. npm传参技巧

    博主今天遇到一个问题,使用vue-cli-serve,想要用shelljs来执行vue-cli-serve,动态给它传“--port xxxx"但是发现”--port“怎么传都穿不进去,后面 ...

  8. 7.26T2某不科学的迷你激光炮

    题目描述 身为课代表的她,下课总愿意帮老师发作业.老师的作业好多好多啊,一天 下来,她下课休息时间也无几了…… 要是天花板上有一只激光炮该多好啊!把作业塞到激光炮里面,轰——一排 同学该都拿到作业了吧 ...

  9. JVM——类加载

    一.什么是类加载? JVM将class字节码文件加载到内存中, 并将这些静态数据转换成方法区中的运行时数据结构,在堆中生成一个代表这个类的java.lang.Class 对象,作为方法区类数据的访问入 ...

  10. allowMultiQueries=true mybatis 要多行sql执行,一定要注意

    allowMultiQueries=true 这个配置已经出现多次问题了,这次由于切换时多数据源,搞配置的同志不知道从哪里copy的配置,只换了我们的链接,我们之前配置了好多配置都丢失了,我的代码中有 ...