zoukankan      html  css  js  c++  java
  • 多线程-Task、await/async

    Task创建无返回值

    Task是.netframwork4.0重新分装的多线程类。原因以前的多线程(thread threadpool)不好用。(.net framwork也是的发展的,现在的EF,刚开始是一个edmx文件,现在的code first,ef轻量级。但是其他有的技术也是死掉了)

    Task具有线程执行的可控性,返回值,代码书写简单,性能好等特点。

    Task创建主要有三种方式

    1、Task参数

    Task t = new Task(() =>
    {
        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine(i);
        }
    });
    t.Start();
    View Code

    Action,Action<object>,object state,CancellationToken,TaskCreationOptions

    Action<object>:task创建有参数的任务,参数只能是object

    object:带有参数委托的参数。

    CancellationToken:线程取消通知

    TaskCreationOptions:控制Task的执行方式

    //
    // 摘要:
    //     指定可控制任务的创建和执行的可选行为的标志。
    [Flags]
    public enum TaskCreationOptions
    {
        //
        // 摘要:
        //     指定应使用默认行为。
        None = 0,
        //
        // 摘要:
        //     提示 System.Threading.Tasks.TaskScheduler 以一种尽可能公平的方式安排任务,这意味着较早安排的任务将更可能较早运行,而较晚安排运行的任务将更可能较晚运行。
        PreferFairness = 1,
        //
        // 摘要:
        //     指定任务将是长时间运行的、粗粒度的操作,涉及比细化的系统更少、更大的组件。它会向 System.Threading.Tasks.TaskScheduler
        //     提示,过度订阅可能是合理的。您可以通过过度订阅创建比可用硬件线程数更多的线程。
        LongRunning = 2,
        //
        // 摘要:
        //     指定将任务附加到任务层次结构中的某个父级。有关详细信息,请参阅 已附加和已分离的子任务。
        AttachedToParent = 4,
        //
        // 摘要:
        //     如果尝试附有子任务到创建的任务,指定 System.InvalidOperationException 将被引发。
        DenyChildAttach = 8,
        //
        // 摘要:
        //     防止环境计划程序被视为已创建任务的当前计划程序。这意味着像 StartNew 或 ContinueWith 创建任务的执行操作将被视为 System.Threading.Tasks.TaskScheduler.Default
        //     当前计划程序。
        HideScheduler = 16
    }
    View Code

    2、Task.Factory.StartNew

    Action,Action<object>,object state,CancellationToken,TaskCreationOptions,TaskScheduler

    TaskScheduler:定义:用于计划所创建的 System.Threading.Tasks.Task 的 System.Threading.Tasks.TaskScheduler。(并看不出来是什么意思,英文意思 任务调度器)

    返回值是一个Task

    Task.Factory.StartNew(() =>
    {
        for (int i = 10; i < 20; i++)
        {
            Console.WriteLine(i);
        }
    });
    View Code

    3、TaskFactory

    Task.Factory是TaskFactory的一个实例。

    TaskFactory tf = new TaskFactory();
    tf.StartNew(() =>
    {
        for (int i = 20; i < 30; i++)
        {
            Console.WriteLine(i);
        }
    });
    View Code

     上面的都是不带返回参数的任务

    Task创建有返回值

    一般项目中的返回类型都会特定的封装一个通用的类型。所有都会有返回值。

    Task<string> t = new Task<string>(GetName);
    t.Start();
    Console.WriteLine(t.Result);
    Console.WriteLine("结束");
    View Code
    public static string GetName()
    {
        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine(i);
        }
        return "臧峰";
    }
    View Code

    注意在获取返回值的时候,t.Result会卡着线程,一只等待线程返回结果。

    Task等待

    1、为什么要等待呢,因为线程大部分都要计算已给结果,所以我们要等待这个结果。

    2、public void Wait(。。。。);

    位于Task类中(实例方法。另外还有几个静态方法)。

    定义:等待 System.Threading.Tasks.Task 完成执行过程(卡着其他线程)。

    参数可以设置等待时间,CancellationToken(任务取消)  等待期间可以取消任务。

    3、public static void WaitAll(params Task[] tasks....);

    定义:等待提供的所有 System.Threading.Tasks.Task 对象完成执行过程。(就是当前线程要等这个参数列表中线程全部执行完后  才执行。注意:waitall前面其他的子线程还是在继续执行)

    参数:等待任务列表,事件,CancellationToken

    4、public static int WaitAny(params Task[] tasks);

    定义:等待提供的任一 System.Threading.Tasks.Task 对象完成执行过程。

    参数:等待任务列表,事件,CancellationToken

    5、Task的属性方法

    AsyncState 就是当任务核心方法带参数的时候,参数值
    Status 任务执行进度的状态
    CreationOptions 任务创建和执行的行为
    Id 任务线程ID
    IsCanceled 任务是否被取消
    IsCompleted 任务是否完成
    IsFaulted 任务是否失败
    Result 任务返回值,该值只适用于有返回参数的任务
    ContinueWith() 线程执行完之后执行另外一个任务
    GetAwaiter() 获取等待这个线程的等待着
    Start() 发起一个任务
    Wait() 当前调用wait的任务线程等待

    这些是常用的

    Task回调
    //------------------------Task回调-------------------------
    Task<int> t = new Task<int>(() =>
    {
        var temp = 0;
        for (int i = 0; i < 10; i++)
        {
            temp += i;
        }
        return temp;
    });
    
    t.ContinueWith<string>((f) =>
    {
        Console.WriteLine(f.Result.ToString());
        return f.Result.ToString();
    });
    t.Start();
    
    
    
    //------------------TaskFactory回调----------------------------
    TaskFactory<int> tf = new TaskFactory<int>();
    var tft = tf.StartNew(() =>
     {
         var temp = 0;
         for (int i = 0; i < 10; i++)
         {
             temp += i;
         }
         return temp;
     });
    tft.ContinueWith<string>((f) =>
    {
        Console.WriteLine(f.Result.ToString());
        return f.Result.ToString();
    });
    
    
    //-----------------------ContinueWhenAll--------------------------
    var cts = new CancellationTokenSource();
    TaskFactory<int> tf1 = new TaskFactory<int>(cts.Token, TaskCreationOptions.AttachedToParent, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
    
    Task<int> t1 = tf1.StartNew(() => { Console.WriteLine(1); return 1; });
    Task<int> t2 = tf1.StartNew(() => { Console.WriteLine(2); return 2; });
    Task<int> t3 = tf1.StartNew(() => { Console.WriteLine(3); return 3; });
    Task<int> t4 = tf1.StartNew(() => { Console.WriteLine(4); return 4; });
    Task<int> t5 = tf1.StartNew(() => { Console.WriteLine(5); return 5; });
    
    
    //意思就是说当这些所有的线程都执行完了,在执行后面的线程
    //第一个参数就是要执行的线程数组
    //第二个参数就是   继续执行的线程
    var tcwa = tf1.ContinueWhenAll(new[] { t1, t2, t3, t4, t5 }, tlReturn =>
     {
         int temp = 0;
         for (int i = 0; i < tlReturn.Length; i++)
         {
             temp += tlReturn[i].Result;
         }
         Console.WriteLine(temp);
         return temp;
     });
    
    var tft1 = tf1.StartNew(() =>
    {
        var temp = 0;
        for (int i = 0; i < 10; i++)
        {
            temp += i;
        }
        Console.WriteLine(temp);
        return temp;
    });
    
    
    //-------------------------FromAsync-----------------------------------
    TaskFactory<long> tfFA = new TaskFactory<long>();
    Func<long> faDel = (() =>
    {
        Console.WriteLine("臧锋");
        return 10;
    });
    tfFA.FromAsync(faDel.BeginInvoke(null, null), (tr) =>
    {
        Console.WriteLine(tr.IsCompleted);
        return 11;
    });
    View Code

    还是先了解一下TaskFactory

    ContinueWhen不会卡当前线程。wait会卡当前线程。

    CancellationToken 任务取消标记
    ContinuationOptions 字面意思 任务延续选择。官方注释不敢恭维。和ContinueWhenAll、ContinueWhenAny有关
    CreationOptions 指定此任务工厂的默认创建选项。(任务执行顺序,是否长时间执行...)
    Scheduler 任务工厂的任务计划程序。表示一个处理将任务排队到线程中的低级工作的对象。
    ContinueWhenAll

    创建一个延续任务,它将在提供的组中的所有任务完成后马上开始。第一个参数P1:线程列表,就是要执行的线程

    必须有调用ContinueWhenAll的TaskFactory创建)    第二个参数P2:回调函数

    (回调函数的第一个参数为第一个参数P1,回调函数第二个参数为返回值,由TaskFactory<>定义的)。

      执行顺序  P1先执行,然后P2执行。   TaskFactory的任务为随机执行

    ContinueWhenAny 创建一个延续任务,它将在提供的组中的任何任务完成后马上开始。
    FromAsync

    创建一个任务,它在指定的 System.IAsyncResult 完成时执行一个结束方法函数。(官方解释有点绕  其实就是

    通过它可以把一个异步的任务转换为一个Task,返回值是一个Task,最后在执行一个回调函数)    

    吐槽一下,官方的翻译是不是都是电脑翻译的。

    StartNew 创建开启任务
    Task取消

    主要是类CancellationTokenSource

    定义:通知 System.Threading.CancellationToken,告知其应被取消。

    可以马上取消,还可以设置时间取消。

    CancellationTokenSource clt = new CancellationTokenSource();
    TaskFactory tf = new TaskFactory(clt.Token);
    tf.StartNew(() =>
    {
        int temp = 0;
        for (int i = 0; i < 10000; i++)
        {
            Thread.Sleep(10);
            if (!clt.IsCancellationRequested)
            {
                temp += i;
            }
            else
            {
                break;
            }
        }
        Console.WriteLine(temp);
    });
    Thread.Sleep(200);
    clt.Cancel();
    View Code
    Task返回值

    具有返回值的是要用泛型  指定返回值类型。

    Task<int> t = new Task<int>(() => 5);
    t.Start();
    Console.WriteLine(t.Result);
    
    
    
    TaskFactory<int> tf = new TaskFactory<int>();
    var  tft= tf.StartNew(() => { return 6; });
    Console.WriteLine(tft.Result);
    View Code

    最后通过Task.Result得到结果。这个操作会卡着线程,一直等待回去结果。

     Parallel并行任务

    1、Parallel  定义:提供对并行循环和区域的支持。静态类,静态方法。他是基于Task,线程池。

    其实是操作循环任务,但是循环的任务都是在平行执行的,没人先后顺序,但是,循环结会等待他们都结束的。

    Parallel.For

    Parallel.ForEach

    Parallel.Invok

    优点:主线程也会参与计算,缺点:就是会waitall   会卡住当前线程。

    2、ParallelLoopResult 定义:提供执行 System.Threading.Tasks.Parallel 循环的完成状态。其实就是For和ForEach的返回值

    3、ParallelLoopState 定义:可用来使 System.Threading.Tasks.Parallel 循环的迭代与其他迭代交互。此类的实例由 Parallel 类提供给每个循环;不能在您的用户代码中创建实例。

    他可以让循环退出 ,通知。  (就像for循环中的bread)

    4、ParallelOptions 定义:存储用于配置 System.Threading.Tasks.Parallel 类的方法的操作的选项。

    //
    // 摘要:
    //     获取或设置与此 System.Threading.Tasks.ParallelOptions 实例关联的 System.Threading.CancellationToken。
    //
    // 返回结果:
    //     与此实例关联的标记。
    public CancellationToken CancellationToken { get; set; }
    //
    // 摘要:
    //     获取或设置此 System.Threading.Tasks.ParallelOptions 实例所允许的最大并行度。
    //
    // 返回结果:
    //     一个表示最大并行度的整数。
    //
    // 异常:
    //   T:System.ArgumentOutOfRangeException:
    //     该属性被设置为 0 或小于 -1 的值。
    public int MaxDegreeOfParallelism { get; set; }
    //
    // 摘要:
    //     获取或设置与此 System.Threading.Tasks.ParallelOptions 实例关联的 System.Threading.Tasks.TaskScheduler。将此属性设置为
    //     null,以指示应使用当前计划程序。
    //
    // 返回结果:
    //     与此实例关联的任务计划程序。
    public TaskScheduler TaskScheduler { get; set; }
    View Code

    可以取消任务,并行的数量(循环中一次并行执行任务的个数,当任务量比较大的时候,一定要指定这个值,不然电脑可能撑不住。但是他是基于线程池的,线程池会帮我们控制一下,但是还是会卡电脑。并行数量控制,导致起的线程是一轮一轮的,每一轮第一个线程ID都一样,用的主线程ID)

    5、总结

     Parallel.For()和Paraller.ForEach()方法在每次迭代中调用相同的代码,而Parallel.Invoke()方法允许同时调用不同的方法。Parallel.ForEach()用于数据并行性,Parallel.Invoke()用于任务并行性;

    主线程可以参与计算,但是要waitall。等待。

    Await Async

    1、出现介绍

    C#5.0                  C#语言版本

    .net fromwork 4.5                框架类库版本FCL

    clr4.0                  CLR版本

    await async他是一个语法糖。什么是语法糖呢,就是c#或者.net fromwork改变但是clr没有改变。

    泛型就不是语法糖,他出现的时候clr重写了,就是使用泛型的地方,  对应的生成相应的类型方法。

    没有task就没有await async

    2、原理

    是一个语法糖,利用一个状态机的概念,通过编译器编译把await后面的代码封装为一个委托进行回调执行,并且await会等待执行完成,状态机就是这一个执行完之后,就movenest执行下一个回调。就是await一层层的嵌套就可以了。

    3、使用

    await只能出现在task前面,await后面的代码变成task的回调。快速的写出了异步回调

    返回值只能是void task  task<>,推荐使用task<>返回值,并封装通用T类型。

    本文代码下载

    补充例子

  • 相关阅读:
    Debian6.0 linux安装redmine
    Backtrack无线WPA2破解过程
    回溯法解决0-1背包问题
    机试笔记8--栈与优先队列
    计算机网络笔记--数据链路层
    计算机网络笔记--物理层--调制
    计算机网络笔记--物理层--编码
    计算机网络笔记--物理层-概述
    机试笔记7--数学问题
    两种简单的字符串匹配算法
  • 原文地址:https://www.cnblogs.com/wudequn/p/7571039.html
Copyright © 2011-2022 走看看