CancellationToken的多种施用

  • 1.前言
  • 二.直言不讳
  • 三.开支条件和中央
    • (壹)并发编制程序概述
    • (贰)异步编制程序基础
    • (三)并行开辟的根基
    • (肆)测试本领
    • (5)集合
    • (6)函数式OOP
    • (7)同步
  • 1.前言
  • 2.直言不讳
  • 3.开销条件和要义
    • (壹)并发编制程序概述
    • (二)异步编制程序基础
    • (三)并行开垦的基本功
    • (肆)测试技巧
    • (5)集合
    • (6)函数式OOP
    • (7)同步

前言

本节任重(英文名:rèn zhòng)而道远介绍异步编程中Task、Async和Await的基础知识。

那是线程安全的末尾一篇了,重要介绍CancellationToken的三种行使。

1.前言

近几年来趁着类别的一段平稳期研读了多数书本,当中《C#并发编制程序杰出实例》给小编的映像或许相比深入的。当然,那恐怕是出于近段日子看的书多数嘴炮大于实际,如《Head
First设计方式》《Cracking the coding
interview》等,所以突然见到1本打着“实例”暗记的书本,依然挺让作者觉着耳目一新。本着分享和深化通晓的目标,笔者特意整理了部分笔记(首借使Web开采中轻便涉及的始末,所以有的章节如数据流,君越X等自作者看了看就间接跳过了),以供审阅学习。语言和本事的魔力,真是不得捉摸

1.前言

多年来趁着项目标一段平稳期研读了成百上千图书,在那之中《C#出现编程杰出实例》给作者的回忆照旧比较深远的。当然,那只怕是由于近段日子看的书许多嘴炮大于实际,如《Head
First设计形式》《Cracking the coding
interview》等,所以突然见到壹本打着“实例”记号的图书,照旧挺让本身认为耳目壹新。本着分享和加剧精晓的目标,作者尤其整理了部分笔记(重要是Web开荒中易于涉及的始末,所以部分章节如数据流,WranglerX等自个儿看了看就径直跳过了),以供审阅学习。语言和技巧的魔力,真是不得捉摸

哪些是异步?

异步处理不用阻塞当前线程来等待处理到位,而是允许承袭操作,直至其它线程将处理完结,并回调文告此线程。

出现编程杰出实例,线程也疯狂。一,ThreadPool间接开发银行线程,传递CancellationToken。

2.直截了当

直白以来都有一种观念是兑现底层架构,编写驱动和斯特林发动机,或然是框架和工具开拓的才是高等开垦职员,做上层应用的人单纯是“码农”,其实能够采纳好平台提供的相关类库,而不是全体用到底层手艺和好完成,开拓出高素质,稳固的应用程序,对本事工夫的考验并不低于开垦底层库,如TPL,async,await等。

贰.开门见山

直白以来都有一种观念是促成底层架构,编写驱动和斯特林发动机,或许是框架和工具开辟的才是尖端开垦职员,做上层应用的人偏偏是“码农”,其实能够使用好平台提供的连带类库,而不是全部选取底层技能和好达成,开辟出高水平,稳固的应用程序,对手艺力量的考验并十分大于开拓底层库,如TPL,async,await等。

异步和八线程

一样点:制止调用线程阻塞,从而进步软件的可响应性。

不同点:

异步操作无须额外的线程负担,并且动用回调的格局张开处理,在设计能够的状态下,处理函数能够不要采纳共享变量(就算不能够完全不用,最起码能够削减
共享变量的数目),缩短了死锁的大概。C#伍.0 .NET4.5今后主要字Async和Await的行使,使得异步编制程序变得不行轻便。

四线程中的处理程序还是是各类推行,然则10二线程的欠缺也如出一辙备受瞩目,线程的施用(滥用)会给系统带来上下文切换的额外负担。并且线程间的共享变量只怕引致死锁的产出。

二,Task运营线程,传递CancellationToken。Task传递格局分为三种,一种通过Task的参数举办传递,另1种通过向线程内传递对象的办法传递CancellationToken。

三.开采条件和要义

3.花费规范和要义

异步应用场景及原理

异步重要行使于IO操作,数据库访问,磁盘操作,Socket访问、HTTP/TCP网络通讯 

由来:对于IO操作并不需求CPU实行过多的持筹握算,那个数据主要透过磁盘举办拍卖,即使实行共同通讯不大概收场,须求成立越来越多的线程能源,线程的数目上下文频仍的切换也是对能源的荒废,针对IO操作不须要独自的分红1个线程来拍卖。

举例表明:

操作:服务器收到HTTP请求对数据库实行操作然后回到

联手处理请求的线程会被封堵,异步处理请求的线程不会阻塞。

金沙注册送58 1

 

三,CancellationToken的回调函数应用。

(壹)并发编程概述

  1. 并发:同时做多件业务
  2. 八线程:并发的一种样式,它利用三个线程来奉行顺序
  3. 并行处理:把正在实施的豁达的任务分割成小块,分配给八个同时运维的线程
  4. 并行处理是八线程的一种,而三十二线程是出新的1种处理格局
  5. 异步编制程序:并发的1种样式,它使用future情势恐怕callback机制,防止止爆发不供给的线程
  6. 异步编制程序的宗旨境念是异步操作:运维了的操作会在一段时间后成功。那几个操作正在施行时,不会卡住原来的线程。运维了那么些操作的线程,能够继续施行其余义务。当操作达成后,会通报它的future,可能调用回调函数,以便让程序知道操作已经终止
  7. await关键字的功用:运行四个将会被实行的Task(该Task就要新线程中运营),并随即回去,所以await所在的函数不会被封堵。当Task实现后,继续实践await后边的代码
  8. 响应式编制程序:并发的一种基于注解的编制程序格局,程序在该情势中对事件作出反应
  9. 毫无用 void 作为 async 方法的回到类型! async 方法能够回去
    void,可是那仅限于编写事件处理程序。3个1般的 async
    方法即便未有再次来到值,要回到 Task,而不是 void
  10. async 方法在初始时以共同方式试行。在 async 方法内部,await
    关键字对它的参数试行3个异步等待。它首先检查操作是不是早已做到,假使成功了,就继续运转(同步情势)。不然,它会中断 async 方法,并回到,留下三个未产生的
    task。一段时间后, 操作达成,async
    办法就死灰复燃运维。
  11. await代码中抛出尤其后,至极会沿着Task方向前进到引用处
  12. 您只要在代码中接纳了异步,最佳一向利用。调用
    异步方法时,应该(在调用停止时)用 await 等待它回到的 task
    对象。一定要防止使用 Task.Wait 或 Task.Result
    方法,因为它们会形成死锁
  13. 线程是七个单身的运行单元,每一种进程之中有八个线程,每种线程能够分级同时实践命令。
    各种线程有和好单身的栈,不过与经过内的其余线程共享内存
  14. 各类.NET应用程序都维护着三个线程池,那种场所下,应用程序差不离不要求活动成立新的线程。你若要为
    COM interop 程序创制 SAT 线程,就得 创造线程,这是唯一供给线程的景观
  15. 线程是低档其余架空,线程池是不怎么高端一点的肤浅
  16. 并发编制程序用到的会聚有两类:并发形成+不可变集合
  17. 大大多产出编制程序技艺都有3个类似点:它们本质上都以函数式的。那里的函数式是用作一种基于函数组合的编制程序形式。函数式的3个编制程序原则是轻易(幸免副成效),另2个是不改变性(指壹段数据无法被修改)
  18. .NET 肆.0
    引进了互动职分库(TPL),完全援救数据交互和天职并行。不过某个能源较少的
    平台(例如手提式有线电话机),经常不帮衬 TPL。TPL 是 .NET 框架自带的

(1)并发编制程序概述

  1. 并发:同时做多件工作
  2. 102线程:并发的一种样式,它应用八个线程来实行顺序
  3. 并行处理:把正在实践的多量的职务分割成小块,分配给几个同时运维的线程
  4. 并行处理是八线程的一种,而多线程是出现的1种处理情势
  5. 异步编制程序:并发的1种方式,它利用future形式大概callback机制,以制止爆发不要求的线程
  6. 异步编制程序的宗旨绪念是异步操作:运维了的操作会在壹段时间后完毕。这一个操作正在进行时,不会堵塞原来的线程。运营了那一个操作的线程,能够继续实行其余任务。当操作完结后,会通告它的future,可能调用回调函数,以便让程序知道操作已经结束
  7. await关键字的功力:运转2个将会被实施的Task(该Task就要新线程中运作),并立时回去,所以await所在的函数不会被封堵。当Task实现后,继续施行await后边的代码
  8. 响应式编制程序:并发的一种基于注脚的编制程序情势,程序在该方式中对事件作出反应
  9. 毫无用 void 作为 async 方法的回来类型! async 方法可以回去
    void,不过那只限于编写事件处理程序。3个普普通通的 async
    方法假使未有重回值,要赶回 Task,而不是 void
  10. 金沙注册送58 ,async 方法在始发时以联合形式实行。在 async 方法内部,await
    关键字对它的参数执行三个异步等待。它首先检查操作是还是不是业已成功,假使成功了,就卫冕运行(同步格局)。不然,它会中断 async 方法,并回到,留下贰个未产生的
    task。一段时间后, 操作完结,async
    艺术就过来运行。
  11. await代码中抛出十分后,十分会沿着Task方向前进到引用处
  12. 你一旦在代码中采纳了异步,最佳向来利用。调用
    异步方法时,应该(在调用停止时)用 await 等待它回到的 task
    对象。一定要幸免选取 Task.Wait 或 Task.Result
    方法,因为它们会导致死锁
  13. 线程是2个独自的运转单元,各个进度之中有五个线程,各类线程能够独家同时实施命令。
    每一种线程有协调独自的栈,然则与经过内的别样线程共享内部存款和储蓄器
  14. 各种.NET应用程序都维护着3个线程池,这种景观下,应用程序大概不必要活动创立新的线程。你若要为
    COM interop 程序创设 SAT 线程,就得 创设线程,那是唯一要求线程的气象
  15. 线程是低档其他悬空,线程池是稍微高等一点的架空
  16. 并发编程用到的集纳有两类:并发形成+不可变集合
  17. 大大多并发编制程序技能都有一个类似点:它们本质上都以函数式的。那里的函数式是作为壹种基于函数组合的编制程序形式。函数式的三个编制程序原则是精简(幸免副效率),另二个是不变性(指一段数据不可能被改变)
  18. .NET 四.0
    引进了互动职责库(TPL),完全扶助数据交互和职分并行。不过有个别能源较少的
    平台(例如手机),经常不援助 TPL。TPL 是 .NET 框架自带的

任务

  在利用职分从前,针对线程的调用许多都用线程池提供的静态方法QueueUserWorkItem,可是这么些函数有众多的范围,个中最大的标题正是未有中间机制能够让开荒者知道操作在怎么着时候做到,也并未有机制在操作完结时获得重临值,微软为了缓解那几个难点引进了任务的定义。

 首先构造四个Task<TResult>对象,并为TResult传递再次回到值,初始职务之后等待它并赶回结果,示例代码:

 1 static void Main(string[] args)
 2         {
 3             Console.WriteLine("开始进行计算");
 4            // ThreadPool.QueueUserWorkItem(Sum, 10);
 5             Task<int> task = new Task<int>(Sum, 100);
 6             task.Start();
 7             //显示等待获取结果
 8             task.Wait();
 9             //调用Result时,等待返回结果
10             Console.WriteLine("程序结果为 Sum = {0}",task.Result);
11             Console.WriteLine("程序结束");
12             Console.ReadLine();
13         }
14 
15         public static int Sum(object i)
16         {
17             var sum = 0;
18             for (var j = 0; j <= (int) i; j++)
19             {
20                 Console.Write("{0} + ",sum);
21                 sum += j;
22             }
23             Console.WriteLine( " = {0}",sum);
24             return sum;
25         }

除了wait等待单个职责外,task还提供了等候四个任务,WaitAny和WaitAll,它阻挡调用线程,直到数组中装有的Task对象完结。

话不多说,请看代码。

(2)异步编制程序基础

  1. 指数退避是1种重试计谋,重试的延迟时间会逐 次增添。在造访 Web
    服务时,最棒的艺术便是使用指数退避,它能够预防服务器被太多的重试阻塞

static async Task<string> DownloadStringWithRetries(string uri)
{
    using (var client = new HttpClient())
    {
        // 第 1 次重试前等 1 秒,第 2 次等 2 秒,第 3 次等 4 秒。
        var nextDelay = TimeSpan.FromSeconds(1);
        for (int i = 0; i != 3; ++i)
        {
            try
            {
                return await client.GetStringAsync(uri);
            }
            catch
            { }

            await Task.Delay(nextDelay);
            nextDelay = nextDelay + nextDelay;
        }

        // 最后重试一次,以便让调用者知道出错信息。
        return await client.GetStringAsync(uri);
    }
}
  1. Task.Delay
    适合用来对异步代码实行单元测试也许完毕重试逻辑。要完结超时功效的话,
    最棒使用 CancellationToken
  2. 怎么样贯彻三个怀有异步具名的联手方法。假若从异步接口或基类承接代码,但愿意用一块的办法来促成它,就会并发那种情景。解决办法是能够运用
    Task.FromResult 方法创设并赶回一个新的 Task 对象,这几个 Task
    对象是早已 完结的,并有钦命的值
  3. 运用 IProgress 和 Progress 类型。编写的 async 方法需求有 IProgress
    参数,其 中 T 是要求告诉的快慢类型,能够来得操作的进程
  4. Task.WhenALl能够等待全体职责达成,而当各种Task抛出卓殊时,能够选用性捕获极度
  5. Task.WhenAny能够等待任1职责实现,使用它就算可以产生超时职务(个中1个Task设为Task.Delay),可是分明用特别的涵盖撤除标志的过期函数处理比较好
  6. 第二章提到async和上下文的标题:在私下认可情形下,三个 async 方法在被
    await
    调用后回复运营时,会在本来的上下文中运作。而丰裕扩充方法ConfigureAwait(false)后,则会在await之后放任上下文

(贰)异步编制程序基础

  1. 指数退避是一种重试攻略,重试的延迟时间会逐 次扩展。在走访 Web
    服务时,最佳的点子正是行使指数退避,它可以卫戍服务器被太多的重试阻塞

static async Task<string> DownloadStringWithRetries(string uri)
{
    using (var client = new HttpClient())
    {
        // 第 1 次重试前等 1 秒,第 2 次等 2 秒,第 3 次等 4 秒。
        var nextDelay = TimeSpan.FromSeconds(1);
        for (int i = 0; i != 3; ++i)
        {
            try
            {
                return await client.GetStringAsync(uri);
            }
            catch
            { }

            await Task.Delay(nextDelay);
            nextDelay = nextDelay + nextDelay;
        }

        // 最后重试一次,以便让调用者知道出错信息。
        return await client.GetStringAsync(uri);
    }
}
  1. Task.Delay
    适合用来对异步代码举行单元测试也许实现重试逻辑。要兑现超时功能的话,
    最佳应用 CancellationToken
  2. 何以贯彻二个具备异步具名的贰只方法。假使从异步接口或基类承继代码,但希望用协同的艺术来达成它,就会现出那种情况。化解办法是足以利用
    Task.FromResult 方法创设并回到三个新的 Task 对象,这么些 Task
    对象是早就 达成的,并有钦赐的值
  3. 应用 IProgress 和 Progress 类型。编写的 async 方法须求有 IProgress
    参数,其 中 T 是索要报告的进程类型,能够展现操作的速度
  4. Task.WhenALl可以等待全部任务成功,而当各个Task抛出至极时,能够选拔性捕获格外
  5. Task.WhenAny能够等待任一职分到位,使用它尽管能够实现超时职责(个中多个Task设为Task.Delay),不过显然用尤其的盈盈打消标识的晚点函数处理相比好
  6. 率先章提到async和上下文的主题素材:在暗中认可意况下,二个 async 方法在被
    await
    调用后恢复生机运行时,会在原先的上下文中运营。而增加扩大方法ConfigureAwait(false)后,则会在await之后扬弃上下文

打消职责

职务的撤废同样运用的是.NET
Framework的正规取消操作方式,首先须要创制1个CancellationTokenSource对象,然后在函数中插手参数CancellationToken,将CancellationTokenSource的Token传递给艺术,然后调用IsCancellationRequested获取是不是业已打消该值实行判断。

 1 static void Main(string[] args)
 2         {
 3             Console.WriteLine("开始进行计算");
 4             // ThreadPool.QueueUserWorkItem(Sum, 10);
 5             var  ctx = new CancellationTokenSource();
 6             var task = new Task<int>(() => Sum(ctx.Token, 100000));
 7             task.Start();
 8             //显示等待获取结果
 9             //task.Wait(ctx.Token);
10             Thread.Sleep(1000);
11             ctx.Cancel();
12             //调用Result时,等待返回结果
13             Console.WriteLine("程序结果为 Sum = {0}", task.Result);
14             Console.WriteLine("程序结束");
15             Console.ReadLine();
16         }
17 
18         public static int Sum(CancellationToken cts, object i)
19         {
20             var sum = 0;        
21             for (var j = 0; j <= (int)i; j++)
22             {
23                 if (cts.IsCancellationRequested) return sum;
24                 Thread.Sleep(50);
25                 Console.Write("{0} + ", sum);
26                 sum += j;
27             }
28             Console.WriteLine(" = {0}", sum);
29             return sum;
30         }
  class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("当前线程{0},当前状态{1}", Thread.CurrentThread.GetHashCode(), Thread.CurrentThread.ThreadState);
            //使用线程池创建线程,然后取消线程
            CancelWithThreadPoolMiniSnippet();
        }
        static CancellationTokenSource cts = new CancellationTokenSource();
        static CancellationToken token = cts.Token;
        static void CancelWithThreadPoolMiniSnippet()
        {
            Console.WriteLine("当前线程{0},当前状态{1}", Thread.CurrentThread.GetHashCode(), Thread.CurrentThread.ThreadState);

            #region 使用QueueUserWorkItem的构造函数,传递cts.Token,但我不喜欢这个模式 跟踪不了状态
            //ThreadPool.QueueUserWorkItem(new WaitCallback(DoSomeWork), ctn);
            #endregion

            #region 使用传递参数的模式 传递CancellationToken,这里的cts.Token是作为Action的参数传递的
            //var action = new Action<object>(DoSomeWork);
            //Task t = new Task(action, ctn);
            //t.Start();
            //Console.WriteLine("开始,当前线程{0},当前状态{1}", t.GetHashCode(), t.Status);
            #endregion

            #region 使用Task的构造函数,传递cts.Token,但CancellationTokenSource要弄成全局变量,否则方法找不到,就取消不了。
            //Task t = new Task(Work, cts.Token);
            //t.Start();
            #endregion

            #region 注册回调函数,当CancellationTokenSource.Cancel()执行后,调用回调函数 
            token.Register(CallBack, true);  //注册回调函数
            Task t = new Task(Work);
            t.Start();
            #endregion

            Thread.SpinWait(5000000);

            cts.Cancel();
            Console.WriteLine("结束,当前线程{0},当前状态{1}", t.GetHashCode(), t.Status);
            Console.Read();
        }


        static void DoSomeWork(object obj)
        {
            CancellationToken token = (CancellationToken)obj;
            for (int i = 0; i < 100000; i++)
            {
                Console.WriteLine(i);
                // Simulating work.
                //Thread.SpinWait(5000000);

                if (token.IsCancellationRequested)
                {

                    break;
                }
            }
        }


        static void Work()
        {

            for (int i = 0; i < 100000; i++)
            {
                Console.WriteLine(i);
                if (token.IsCancellationRequested)
                {

                    break;
                }
            }
        }

        static void CallBack()
        {

            Console.WriteLine("I'm call back!"   );
        }
    }

(三)并行开垦的底蕴

  1. Parallel 类有三个简练的积极分子
    Invoke,可用以须要并行调用一堆措施,并且那么些措施(大多数)是相互独立的

static void ProcessArray(double[] array)
{
    Parallel.Invoke(
    () => ProcessPartialArray(array, 0, array.Length / 2),
    () => ProcessPartialArray(array, array.Length / 2,array.Length));
}

static void ProcessPartialArray(double[] array, int begin, int end)
{
    // 计算密集型的处理过程 ...  
}
  1. 在现身编制程序中,Task类有多个效益:作为并行任务,或作为异步职务。并行职责能够运用
    阻塞的积极分子函数,例如 Task.Wait、Task.Result、Task.WaitAll 和
    Task.WaitAny。并行职责平时也运用 AttachedToParent
    来树立职务之间的“父 / 子”关系。并行职责的创导必要 用 Task.Run 恐怕Task.Factory.StartNew。
  2. 反而的,异步职分应该制止选拔阻塞的分子函数,而应当使用
    await、Task.WhenAll 和 Task. WhenAny。异步职分不选择AttachedToParent,但足以经过 await 另三个职分,建立一种隐 式的“父 /
    子”关系。

(三)并行开垦的功底

  1. Parallel 类有贰个轻便易行的成员
    Invoke,可用于供给并行调用一群措施,并且那一个方法(大多数)是相互独立的

static void ProcessArray(double[] array)
{
    Parallel.Invoke(
    () => ProcessPartialArray(array, 0, array.Length / 2),
    () => ProcessPartialArray(array, array.Length / 2,array.Length));
}

static void ProcessPartialArray(double[] array, int begin, int end)
{
    // 计算密集型的处理过程 ...  
}
  1. 在产出编制程序中,Task类有三个职能:作为并行任务,或作为异步职分。并行职分能够运用
    阻塞的分子函数,例如 Task.Wait、Task.Result、Task.WaitAll 和
    Task.WaitAny。并行任务平常也使用 AttachedToParent
    来建立职务之间的“父 / 子”关系。并行职务的创造要求 用 Task.Run 或者Task.Factory.StartNew。
  2. 相反的,异步职分应该制止选择阻塞的积极分子函数,而应该使用
    await、Task.WhenAll 和 Task. WhenAny。异步职责不应用
    AttachedToParent,但足以透过 await 另四个职分,建立①种隐 式的“父 /
    子”关系。

职责成功后活动运行新职务

实际上的支出应用中,平常出现三遍职务到位后即时运转此外一个职务,并且不可见使线程阻塞,在职责未有完毕时调用result会使程序阻塞,无法查看职分的实施进程,TASK提供了贰个办法ContinueWith,它不会堵塞任何线程,当第2个职分到位时,会及时运营第3个职分。

 1 static void Main(string[] args)
 2         {
 3             Console.WriteLine("开始进行计算");
 4             // ThreadPool.QueueUserWorkItem(Sum, 10);
 5             var  ctx = new CancellationTokenSource();
 6             var task = new Task<int>(() => Sum(ctx.Token, 100000));
 7             task.Start();
 8             var cwt = task.ContinueWith(p =>
 9             {
10                 Console.WriteLine("task result ={0} ",task.Result);
11             });
12             //显示等待获取结果
13             //task.Wait(ctx.Token);
14             Thread.Sleep(1000);
15             ctx.Cancel();
16             //调用Result时,等待返回结果
17             Console.WriteLine("程序结果为 Sum = {0}", task.Result);
18             Console.WriteLine("程序结束");
19             Console.ReadLine();
20         }
21 
22         public static int Sum(CancellationToken cts, object i)
23         {
24             var sum = 0;        
25             for (var j = 0; j <= (int)i; j++)
26             {
27                 if (cts.IsCancellationRequested) return sum;
28                 Thread.Sleep(50);
29                 Console.Write("{0} + ", sum);
30                 sum += j;
31             }
32             Console.WriteLine(" = {0}", sum);
33             return sum;
34         }

代码内实行结果如下,该结果为CancellationToken的回调函数应用:

(四)测试技艺

  1. MSTest从Visual Studio2011 版本开头帮助 async Task 类型的单元测试
  2. 1经单元测试框架不辅助 async Task
    类型的单元测试,就必要做壹些附加的修改技巧等待异步操作。当中1种做法是行使
    Task.Wait,并在有错误时拆开 AggregateException 对象。作者的提出是使用
    NuGet 包 Nito.AsyncEx 中的 AsyncContext 类

那边附上三个ABP中贯彻的可操作AsyncHelper类,便是基于AsyncContext实现

    /// <summary>
    /// Provides some helper methods to work with async methods.
    /// </summary>
    public static class AsyncHelper
    {
        /// <summary>
        /// Checks if given method is an async method.
        /// </summary>
        /// <param name="method">A method to check</param>
        public static bool IsAsyncMethod(MethodInfo method)
        {
            return (
                method.ReturnType == typeof(Task) ||
                (method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>))
                );
        }

        /// <summary>
        /// Runs a async method synchronously.
        /// </summary>
        /// <param name="func">A function that returns a result</param>
        /// <typeparam name="TResult">Result type</typeparam>
        /// <returns>Result of the async operation</returns>
        public static TResult RunSync<TResult>(Func<Task<TResult>> func)
        {
            return AsyncContext.Run(func);
        }

        /// <summary>
        /// Runs a async method synchronously.
        /// </summary>
        /// <param name="action">An async action</param>
        public static void RunSync(Func<Task> action)
        {
            AsyncContext.Run(action);
        }
    }
  1. 在 async 代码中,关键准则之1正是防止选用 async
    void。小编可怜建议我们在对 async void
    方法做单元测试时张开代码重构,而不是使用 AsyncContext。

(四)测试本事

  1. MSTest从Visual Studio二〇一二 版本伊始帮助 async Task 类型的单元测试
  2. 假如单元测试框架不协理 async Task
    类型的单元测试,就须要做一些附加的修改工夫等待异步操作。在那之中1种做法是使用
    Task.Wait,并在有错误时拆开 AggregateException 对象。作者的提出是选拔NuGet 包 Nito.AsyncEx 中的 AsyncContext 类

此间附上3个ABP中落到实处的可操作AsyncHelper类,正是依照AsyncContext实现

    /// <summary>
    /// Provides some helper methods to work with async methods.
    /// </summary>
    public static class AsyncHelper
    {
        /// <summary>
        /// Checks if given method is an async method.
        /// </summary>
        /// <param name="method">A method to check</param>
        public static bool IsAsyncMethod(MethodInfo method)
        {
            return (
                method.ReturnType == typeof(Task) ||
                (method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>))
                );
        }

        /// <summary>
        /// Runs a async method synchronously.
        /// </summary>
        /// <param name="func">A function that returns a result</param>
        /// <typeparam name="TResult">Result type</typeparam>
        /// <returns>Result of the async operation</returns>
        public static TResult RunSync<TResult>(Func<Task<TResult>> func)
        {
            return AsyncContext.Run(func);
        }

        /// <summary>
        /// Runs a async method synchronously.
        /// </summary>
        /// <param name="action">An async action</param>
        public static void RunSync(Func<Task> action)
        {
            AsyncContext.Run(action);
        }
    }
  1. 在 async 代码中,关键准则之壹正是制止使用 async
    void。笔者分外提议大家在对 async void
    方法做单元测试时实行代码重构,而不是选用 AsyncContext。

Async&Await 轻松利用

动用Async&Await的根本目标是方便开展异步操作,因为.net
肆.0
以前进行异步操作时相比较复杂的,重借使经过调用微软提供的异步回调方法开始展览编制程序,假如遇上供给团结达成的办法显得极度头疼,.net的逐1版本都有投机首选的本事,像.NET一.第11中学的委托,.NET二.0中的泛型,.NET叁.0中的Linq,.NET四.0中的Dynimac,.net四.5首要推荐的正是异步编制程序,大家只须要通晓TASK+异步函数就可以兑现异步编制程序。

async:告诉CLQashqai这是2个异步函数。

await:  将Task<TResult>再次回到值的函数实行异步处理。

 

以身作则指标:获取网址JS代码,并在界面呈现。

 1  private static async Task<string> DownloadStringWithRetries(string uri)
 2         {
 3             using (var client = new HttpClient())
 4             {
 5                 // 第1 次重试前等1 秒,第2 次等2 秒,第3 次等4 秒。
 6                 var nextDelay = TimeSpan.FromSeconds(1);
 7                 for (int i = 0; i != 3; ++i)
 8                 {
 9                     try
10                     {
11                         return await client.GetStringAsync(uri);
12                     }
13                     catch
14                     {
15                     }
16                     await Task.Delay(nextDelay);
17                     nextDelay = nextDelay + nextDelay;
18                 }
19                 // 最后重试一次,以便让调用者知道出错信息。
20                 return await client.GetStringAsync(uri);
21             }
22         }

 1  static  void Main(string[] args)
 2         {
 3             Console.WriteLine("获取百度数据");
 4             ExecuteAsync();
 5             Console.WriteLine("线程结束");
 6             Console.ReadLine();
 7         }
 8 
 9         public static async void ExecuteAsync()
10         {
11            string text = await DownloadStringWithRetries("http://wwww.baidu.com");
12            Console.WriteLine(text);
13         }

运转结果发现,首先得到百度数据,线程结束,末了呈现HTML代码,那是因为异步开启了新的线程,并不会产生线程阻塞。

 

金沙注册送58 2

(5)集合

  1. 线程安全集合是可同时被七个线程修改的可变集合。线程安全集合混合使用了细粒度锁定和无锁技术,以保障线程被打断的时间最短(经常状态下是根本不打断)。对数不胜数线程安全集合实行枚举操作时,内部创设了该集合的1个快速照相(snapshot),并对那些快速照相举办枚举操作。线程安全集合的关键优点是多少个线程能够安全地对其打开走访,而代码只会被卡住非常短的命宫,或根本不打断

  2. ConcurrentDictionary是数据结构中的精品,它是线程安全的,混合使用了细粒度锁定和无锁技巧,以有限支撑当先八分之四景观下能进行火速访问.

  3. ConcurrentDictionary 内置了AddOrUpdate, TryRemove,
    TryGetValue等艺术。假使多个线程读写七个共享集合,使用ConcurrentDictionary是最合适的,即使不会频仍修改,那就更适合选择ImmutableDictionary。而只假如1些线程只添美成分,壹些线程只移除成分,最棒使用生产者/消费者集合

(5)集合

  1. 线程安全集合是可同时被八个线程修改的可变集合。线程安全集合混合使用了细粒度锁定和无锁技能,以确认保证线程被打断的光阴最短(日常景况下是一直不封堵)。对大多线程安全集合进行枚举操作时,内部创设了该集合的一个快速照相(snapshot),并对这些快速照相实行枚举操作。线程安全集合的最主要优点是多个线程能够安枕而卧地对其进行访问,而代码只会被打断相当短的时间,或根本不打断

  2. ConcurrentDictionary<TKey,
    TValue>是数据结构中的精品,它是线程安全的,混合使用了细粒度锁定和无锁技艺,以保证超过半数动静下能拓展火速访问.

  3. ConcurrentDictionary<TKey, 电视alue> 内置了AddOrUpdate,
    TryRemove,
    TryGetValue等艺术。假诺八个线程读写二个共享集合,使用ConcurrentDictionary<TKey,
    TValue>是最合适的,即便不会频仍修改,那就更符合利用ImmutableDictionary<TKey,
    电视alue>。而只就算部分线程只添新币素,一些线程只移除成分,最棒使用生产者/消费者集合

到此NET Framework四.0里的线程安全就都讲完了。。。。。。。

(6)函数式OOP

  1. 异步编制程序是函数式的(functional),.NET
    引入的async让开发者进行异步编制程序的时候也能用进程式编制程序的思辨来举办思想,不过在里面贯彻上,异步编制程序照旧是函数式的

    远大说过,世界既是进度式的,也是函数式的,不过究竟是函数式的

  2. 能够用await等待的是一个类(如Task对象),而不是一个方法。能够用await等待有些方法重回的Task,无论它是否async方法。

  3. 类的构造函数里是不可能举办异步操作的,一般能够行使如下方法。相应的,大家得以经过var instance=new Program.CreateAsync();

    class Program
    {
        private Program()
        {

        }

        private async Task<Program> InitializeAsync()
        {
            await Task.Delay(TimeSpan.FromSeconds(1));

            return this;
        }

        public static Task<Program> CreateAsync()
        {
            var result = new Program();

            return result.InitializeAsync();
        }

    }
  1. 在编写制定异步事件处理器时,事件参数类最佳是线程安全的。要成功这一点,最轻松易行的法子就是让它成为不可变的(即把具备的习性都设为只读)

(6)函数式OOP

  1. 异步编制程序是函数式的(functional),.NET
    引进的async让开辟者实行异步编制程序的时候也能用进程式编制程序的思考来张开观念,但是在其间贯彻上,异步编制程序如故是函数式的

    宏大说过,世界既是进程式的,也是函数式的,可是终究是函数式的

  2. 能够用await等待的是三个类(如Task对象),而不是1个措施。能够用await等待有个别方法重返的Task,无论它是或不是async方法。

  3. 类的构造函数里是不能够开始展览异步操作的,一般能够行使如下方法。相应的,大家能够透过var instance=new Program.CreateAsync();

    class Program
    {
        private Program()
        {

        }

        private async Task<Program> InitializeAsync()
        {
            await Task.Delay(TimeSpan.FromSeconds(1));

            return this;
        }

        public static Task<Program> CreateAsync()
        {
            var result = new Program();

            return result.InitializeAsync();
        }

    }
  1. 在编写制定异步事件处理器时,事件参数类最佳是线程安全的。要产生那一点,最简易的方法就是让它造成不可变的(即把装有的质量都设为只读)

固然第3篇小说是201三年,固然历时近5年,但请相信笔者,代码早在伍年前就已经写完呀。只是自笔者一向一贯一向没配文字发出来。。。。。。

(7)同步

  1. 一路的项目首要有三种:通讯和数据敬爱

  2. 假若上边几个规范都满意,就要求用一道来保证共享的数码

  • 多段代码正在出现运行
  • 这几段代码在访问(读或写)同3个多少
  • 起码有一段代码在修改(写)数据
  1. 观测以下代码,明确其同台和平运动作处境

class SharedData
{
    public int Value { get; set; }
}

async Task ModifyValueAsync(SharedData data)
{
    await Task.Delay(TimeSpan.FromSeconds(1));
    data.Value = data.Value + 1;
}

// 警告:可能需要同步,见下面的讨论。
async Task<int> ModifyValueConcurrentlyAsync()
{
    var data = new SharedData();

    // 启动三个并发的修改过程。
    var task1 = ModifyValueAsync(data);
    var task2 = ModifyValueAsync(data);
    var task3 = ModifyValueAsync(data);

    await Task.WhenAll(task1, task2, task3);

    return data.Value;
}

本例中,运转了多少个并发运转的修改过程。须要共同啊?答案是“看情况”。假使能显明这么些艺术是在 GUI 或 ASP.NET
上下文中调用的(或同近年来间内只同意1段代码运维的任
何别的上下文),那就不须要一齐,因为那七个修改数据经过的运作时刻是互不相同的。
例如,要是它在 GUI 上下文中运作,就唯有3个 UI
线程能够运转那个多少修改进程,因此1段时间内只好运转一个进程。因而,纵然可以规定是“同一时半刻间只运维壹段代码”的
上下文,那就不供给共同。可是只要从线程池线程(如
Task.Run)调用那些法子,就要求联合了。在那种状态下,那三个数据修改进度会在单独的线程池线程中运转,并且同时修改
data.Value,由此必须一齐地拜会 data.Value。

  1. 不足变类型自己正是线程安全的,修改二个不可变集合是不容许的,纵然使用三个Task.Run向聚集中添扩充少,也并不要求同步操作

  2. 线程安全集合(例如
    ConcurrentDictionary)就全盘区别了。与不可变集合分歧,线程安
    全集合是足以修改的。线程安全集合本人就蕴含了装有的一路功能

  3. 至于锁的行使,有4条主要的规则

  • 范围锁的成效范围(例如把lock语句使用的指标设为私有成员)
  • 文档中写清锁的功能内容
  • 锁范围内的代码尽量少(锁定期不用开始展览围堵操作)
  • 在调节锁的时候绝不运营随意的代码(不要在言辞中调用事件处理,调用虚拟方法,调用委托)
  1. 借使要求异步锁,请尝试 SemaphoreSlim

  2. 绝不在 ASP. NET 中央银行使 Task.Run,这是因为在 ASP.NET
    中,处理请求的代码本来便是在线程池线程中运作的,强行把它内置另1个线程池线程常常会适得其反

(七) 实用本领

  1. 次第的三个部分共享了叁个财富,现在要在率先次访问该财富时对它初阶化

static int _simpleValue;

static readonly Lazy<Task<int>> MySharedAsyncInteger = 
    new Lazy<Task<int>>(() => 
    Task.Run(async () =>
        {
            await Task.Delay(TimeSpan.FromSeconds(2));

            return _simpleValue++;
        }));

async Task GetSharedIntegerAsync()
{
    int sharedValue = await MySharedAsyncInteger.Value;
}

(7)同步

  1. 一同的类别首要有二种:通讯和数据珍惜

  2. 倘使上面四个标准都知足,就要求用联合来保证共享的数码

  • 多段代码正在出现运维
  • 这几段代码在做客(读或写)同1个数码
  • 最少有壹段代码在修改(写)数据
  1. 观测以下代码,显明其同台和平运动作情状

class SharedData
{
    public int Value { get; set; }
}

async Task ModifyValueAsync(SharedData data)
{
    await Task.Delay(TimeSpan.FromSeconds(1));
    data.Value = data.Value + 1;
}

// 警告:可能需要同步,见下面的讨论。
async Task<int> ModifyValueConcurrentlyAsync()
{
    var data = new SharedData();

    // 启动三个并发的修改过程。
    var task1 = ModifyValueAsync(data);
    var task2 = ModifyValueAsync(data);
    var task3 = ModifyValueAsync(data);

    await Task.WhenAll(task1, task2, task3);

    return data.Value;
}

本例中,运行了多个并发运转的修改进程。要求一齐啊?答案是“看事态”。如若能显然那么些艺术是在 GUI 或 ASP.NET
上下文中调用的(或同暂且间内只允许一段代码运维的任
何别的上下文),那就不要求一齐,因为那多个修改数据经过的运作时刻是互区别的。
例如,如若它在 GUI 上下文中运作,就唯有1个 UI
线程可以运转那些数量修改进程,因此1段时间内只好运转八个经过。因而,假诺可以分明是“同目前间只运维1段代码”的
上下文,那就不须求一同。然则即使从线程池线程(如
Task.Run)调用这几个办法,就必要1块了。在这种状态下,那多少个数据修改过程会在单身的线程池线程中运作,并且还要修改
data.Value,因而必须联合地拜会 data.Value。

  1. 不足变类型本人正是线程安全的,修改1个不可变集合是不容许的,固然使用多少个Task.Run向聚集中添扩充少,也并不需求同步操作

  2. 线程安全集合(例如
    ConcurrentDictionary)就全盘两样了。与不可变集合差异,线程安
    全集合是能够修改的。线程安全集合本身就包涵了具备的①道功用

  3. 关于锁的运用,有四条至关心注重要的规则

  • 限制锁的作用范围(例如把lock语句使用的靶子设为私有成员)
  • 文档中写清锁的职能内容
  • 锁范围内的代码尽量少(锁定期毫无进行围堵操作)
  • 在调整锁的时候不要运维随意的代码(不要在言语中调用事件处理,调用虚拟方法,调用委托)
  1. 若果急需异步锁,请尝试 SemaphoreSlim

  2. 无须在 ASP. NET 中运用 Task.Run,那是因为在 ASP.NET
    中,处理请求的代码本来正是在线程池线程中运转的,强行把它放到另二个线程池线程平日会弄巧成拙

(⑦) 实用手艺

  1. 先后的几个部分共享了3个财富,以往要在首先次访问该财富时对它起初化

static int _simpleValue;

static readonly Lazy<Task<int>> MySharedAsyncInteger = 
    new Lazy<Task<int>>(() => 
    Task.Run(async () =>
        {
            await Task.Delay(TimeSpan.FromSeconds(2));

            return _simpleValue++;
        }));

async Task GetSharedIntegerAsync()
{
    int sharedValue = await MySharedAsyncInteger.Value;
}

可是,也或然是多年来写文字的力量有着晋级,所以就马到功成了4和5。

要不然那线程安全的文章或者还要拖。。。。。。。。哈哈

 后记

在NET
Framework4.6里,微软提供了async和await语法,也是关于线程安全,作者将会在新的语法相关小说里上课async和await的用法。

 


注:此小说为原创,欢迎转发,请在小说页面明显地方给出此文链接!
若你以为那篇文章还不易请点击下右下角的推荐,非凡多谢!

相关文章

网站地图xml地图