zoukankan      html  css  js  c++  java
  • C#实现多线程的方式:Task——任务

    简介

      .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(3000);
    }
    
    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(3000);
    }

       连续任务通过在任务上调用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(2000);
          Console.WriteLine(parent.Status);
          Thread.Sleep(4000);
          Console.WriteLine(parent.Status);
      }
      
      static void ParentTask()
      {
          Console.WriteLine("task id {0}", Task.CurrentId);
          var child = new Task(ChildTask);
          child.Start();
          Thread.Sleep(1000);
          Console.WriteLine("parent started child");
      }
      
      static void ChildTask()
      {
          Console.WriteLine("child");
          Thread.Sleep(5000);
          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>(8, 3));
    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

  • 相关阅读:
    PHP 大小写转换、首字母大写、每个单词首字母大写转换相关函数
    【论文学习4】BiSample: Bidirectional Sampling for Handling Missing Data with Local Differential Privacy
    【论文学习3】Local Differential Privacy for Deep Learning
    【论文学习2】 Differential Privacy Reinforcement Learning
    深度学习中的优化算法
    Spatial crowdsourcing
    “pip install tensorflow ”出现错误
    python或pip'不是内部或外部命令”
    pip install torch出现错误
    打不开gitHub的解决方法
  • 原文地址:https://www.cnblogs.com/swfpt/p/6847339.html
Copyright © 2011-2022 走看看