zoukankan      html  css  js  c++  java
  • 8天玩转并行开发——第二天 Task的使用

         在我们了解Task之前,如果我们要使用多核的功能可能就会自己来开线程,然而这种线程模型在.net 4.0之后被一种称为基于

    “任务的编程模型”所冲击,因为task会比thread具有更小的性能开销,不过大家肯定会有疑惑,任务和线程到底有什么区别?

    1:任务是架构在线程之上的,也就是说任务最终还是要抛给线程去执行。

    2:任务跟线程不是一对一的关系,比如开10个任务并不是说会开10个线程,这一点任务有点类似线程池,但是任务相比线程池有很小

          的开销和精确的控制。

    一:Task

    1. 最简单的使用

      开启task有两种方式:

    <1> 实例化Task

    1    //第一种方式开启
    2 var task1 = new Task(() =>
    3 {
    4 Run1();
    5 });

    <2>从工厂中创建

    1   var task2 = Task.Factory.StartNew(() =>
    2 {
    3 Run2();
    4 });


    是的,同样两种方法都可以创建,我们肯定会想两者是不是多多少少有点区别呢?好的,下面我们举个例子看分晓。

    复制代码
     1 using System;
    2 using System.Threading;
    3 using System.Threading.Tasks;
    4 using System.Diagnostics;
    5 using System.Collections.Generic;
    6
    7 class Program
    8 {
    9 static void Main(string[] args)
    10 {
    11 //第一种方式开启
    12 var task1 = new Task(() =>
    13 {
    14 Run1();
    15 });
    16
    17 //第二种方式开启
    18 var task2 = Task.Factory.StartNew(() =>
    19 {
    20 Run2();
    21 });
    22
    23 Console.WriteLine("调用start之前**************************** ");
    24
    25 //调用start之前的“任务状态”
    26 Console.WriteLine("task1的状态:{0}", task1.Status);
    27
    28 Console.WriteLine("task2的状态:{0}", task2.Status);
    29
    30 task1.Start();
    31
    32 Console.WriteLine(" 调用start之后****************************");
    33
    34 //调用start之前的“任务状态”
    35 Console.WriteLine(" task1的状态:{0}", task1.Status);
    36
    37 Console.WriteLine("task2的状态:{0}", task2.Status);
    38
    39 //主线程等待任务执行完
    40 Task.WaitAll(task1, task2);
    41
    42 Console.WriteLine(" 任务执行完后的状态****************************");
    43
    44 //调用start之前的“任务状态”
    45 Console.WriteLine(" task1的状态:{0}", task1.Status);
    46
    47 Console.WriteLine("task2的状态:{0}", task2.Status);
    48
    49 Console.Read();
    50 }
    51
    52 static void Run1()
    53 {
    54 Thread.Sleep(1000);
    55 Console.WriteLine(" 我是任务1");
    56 }
    57
    58 static void Run2()
    59 {
    60 Thread.Sleep(2000);
    61 Console.WriteLine("我是任务2");
    62 }
    63 }
    复制代码

    ①:从图中可以看出两种task实例的简略生命周期。

    Created:表示默认初始化任务,但是我们发现“工厂创建的”实例直接跳过。

    WaitingToRun: 这种状态表示等待任务调度器分配线程给任务执行。

    RanToCompletion:任务执行完毕。

    ②:我们发现task的使用跟Thread很相似,就连waitAll的方法使用也一样,刚才也说了,任务是架构在线程之上,那么我们用VS里面的

          “并行任务”看一看,快捷键Ctrl+D,K,或者找到“调试"->"窗口“->"并行任务“,我们在WaitAll方法处插入一个断点,最终我们发现

          任务确实托管给了线程。

    2. 取消任务

       我们知道task是并行计算的,比如说主线程在某个时刻由于某种原因要取消某个task的执行,我们能做到吗? 当然我们可以做到。

    在4.0中给我们提供一个“取消标记”叫做CancellationTokenSource.Token,在创建task的时候传入此参数,就可以将主线程和任务相

    关联,然后在任务中设置“取消信号“叫做ThrowIfCancellationRequested来等待主线程使用Cancel来通知,一旦cancel被调用。task将会

    抛出OperationCanceledException来中断此任务的执行,最后将当前task的Status的IsCanceled属性设为true。看起来是不是很抽象,

    没关系,上代码说话。

    复制代码
     1 using System;
    2 using System.Threading;
    3 using System.Threading.Tasks;
    4 using System.Diagnostics;
    5 class Program
    6 {
    7 static void Main(string[] args)
    8 {
    9 var cts = new CancellationTokenSource();
    10 var ct = cts.Token;
    11
    12 Task task1 = new Task(() => { Run1(ct); }, ct);
    13
    14 Task task2 = new Task(Run2);
    15
    16 try
    17 {
    18 task1.Start();
    19 task2.Start();
    20
    21 Thread.Sleep(1000);
    22
    23 cts.Cancel();
    24
    25 Task.WaitAll(task1, task2);
    26 }
    27 catch (AggregateException ex)
    28 {
    29 foreach (var e in ex.InnerExceptions)
    30 {
    31 Console.WriteLine(" hi,我是OperationCanceledException:{0} ", e.Message);
    32 }
    33
    34 //task1是否取消
    35 Console.WriteLine("task1是不是被取消了? {0}", task1.IsCanceled);
    36 Console.WriteLine("task2是不是被取消了? {0}", task2.IsCanceled);
    37 }
    38
    39 Console.Read();
    40 }
    41
    42 static void Run1(CancellationToken ct)
    43 {
    44 ct.ThrowIfCancellationRequested();
    45
    46 Console.WriteLine("我是任务1");
    47
    48 Thread.Sleep(2000);
    49
    50 ct.ThrowIfCancellationRequested();
    51
    52 Console.WriteLine("我是任务1的第二部分信息");
    53 }
    54
    55 static void Run2()
    56 {
    57 Console.WriteLine("我是任务2");
    58 }
    59 }
    复制代码

    从图中可以看出

    ①:Run1中的Console.WriteLine("我是任务1的第二部分信息"); 没有被执行。

    ②:Console.WriteLine("task1是不是被取消了? {0}", task1.IsCanceled); 状态为True。

    也就告诉我们Run1中途被主线程中断执行,我们coding的代码起到效果了。

    3. 获取任务的返回值

      我们以前写线程的时候注册的方法一般都是void类型,如果主线程要从工作线程中获取数据一般采用的手段是“委托+事件”的模式,然而

    在Task中有两种方式可以解决。

    <1>  现在我们的实例化是采用Task<TResult>的形式,其中TResult就是当前task执行后返回的结果,下面举得例子是t2任务获取

             t1的执行结果。

    复制代码
     1 using System;
    2 using System.Threading;
    3 using System.Threading.Tasks;
    4 using System.Diagnostics;
    5 using System.Collections.Generic;
    6
    7 class Program
    8 {
    9 static void Main(string[] args)
    10 {
    11 //执行task1
    12 var t1 = Task.Factory.StartNew<List<string>>(() => { return Run1(); });
    13
    14 t1.Wait();
    15
    16 var t2 = Task.Factory.StartNew(() =>
    17 {
    18 Console.WriteLine("t1集合中返回的个数:" + string.Join(",", t1.Result));
    19 });
    20
    21 Console.Read();
    22 }
    23
    24 static List<string> Run1()
    25 {
    26 return new List<string> { "1", "4", "8" };
    27 }
    28 }
    复制代码


    <2>采用ContinueWith方法,很有意思,现在我们将上面的方法改造一下。

    复制代码
     1 using System;
    2 using System.Threading;
    3 using System.Threading.Tasks;
    4 using System.Diagnostics;
    5 using System.Collections.Generic;
    6
    7 class Program
    8 {
    9 static void Main(string[] args)
    10 {
    11 //执行task1
    12 var t1 = Task.Factory.StartNew<List<string>>(() => { return Run1(); });
    13
    14 var t2 = t1.ContinueWith((i) =>
    15 {
    16 Console.WriteLine("t1集合中返回的个数:" + string.Join(",", i.Result));
    17 });
    18
    19 Console.Read();
    20 }
    21
    22 static List<string> Run1()
    23 {
    24 return new List<string> { "1", "4", "8" };
    25 }
    26 }
    复制代码

    4:ContinueWith结合WaitAll来玩一把

        当这两者结合起来,我们就可以玩一些复杂一点的东西,比如说现在有7个任务,其中t1需要串行,t2-t3可以并行,t4需要串行,t5-t6并行,

    t7串行。

    好了,我们上一下代码说话,下面代码没有实际意思,纯属演示。

    复制代码
     1 using System;
    2 using System.Threading;
    3 using System.Threading.Tasks;
    4 using System.Diagnostics;
    5 using System.Collections.Generic;
    6 using System.Collections.Concurrent;
    7
    8 class Program
    9 {
    10 static void Main(string[] args)
    11 {
    12 ConcurrentStack<int> stack = new ConcurrentStack<int>();
    13
    14 //t1先串行
    15 var t1 = Task.Factory.StartNew(() =>
    16 {
    17 stack.Push(1);
    18 stack.Push(2);
    19 });
    20
    21 //t2,t3并行执行
    22 var t2 = t1.ContinueWith(t =>
    23 {
    24 int result;
    25
    26 stack.TryPop(out result);
    27 });
    28
    29 //t2,t3并行执行
    30 var t3 = t1.ContinueWith(t =>
    31 {
    32 int result;
    33
    34 stack.TryPop(out result);
    35 });
    36
    37 //等待t2和t3执行完
    38 Task.WaitAll(t2, t3);
    39
    40
    41 //t4串行执行
    42 var t4 = Task.Factory.StartNew(() =>
    43 {
    44 stack.Push(1);
    45 stack.Push(2);
    46 });
    47
    48 //t5,t6并行执行
    49 var t5 = t4.ContinueWith(t =>
    50 {
    51 int result;
    52
    53 stack.TryPop(out result);
    54 });
    55
    56 //t5,t6并行执行
    57 var t6 = t4.ContinueWith(t =>
    58 {
    59 int result;
    60
    61 //只弹出,不移除
    62 stack.TryPeek(out result);
    63 });
    64
    65 //临界区:等待t5,t6执行完
    66 Task.WaitAll(t5, t6);
    67
    68 //t7串行执行
    69 var t7 = Task.Factory.StartNew(() =>
    70 {
    71 Console.WriteLine("当前集合元素个数:" + stack.Count);
    72 });
    73
    74 Console.Read();
    75 }
    76 }
    复制代码

  • 相关阅读:
    668. Kth Smallest Number in Multiplication Table
    658. Find K Closest Elements
    483. Smallest Good Base
    475. Heaters
    454. 4Sum II
    441. Arranging Coins
    436. Find Right Interval
    410. Split Array Largest Sum
    392. Is Subsequence
    378. Kth Smallest Element in a Sorted Matrix
  • 原文地址:https://www.cnblogs.com/Jeely/p/10999319.html
Copyright © 2011-2022 走看看