线程安全使用,并发编制程序精湛实例

 CancellationToken的二种利用

  • 1.前言
  • 2.开门见山
  • 三.付出规范和大旨
    • (壹)并发编制程序概述
    • (2)异步编制程序基础
    • (三)并行开荒的基本功
    • (四)测试技艺
    • (5)集合
    • (6)函数式OOP
    • (7)同步
  • 1.前言
  • 二.开门见山
  • 3.支出条件和核心
    • (一)并发编程概述
    • (2)异步编制程序基础
    • (三)并行开垦的根基
    • (四)测试手艺
    • (5)集合
    • (6)函数式OOP
    • (7)同步

前言

本节任重(Ren Zhong)而道远介绍异步编制程序中Task、Async和Await的基础知识。

这是线程安全的最终一篇了,首要介绍CancellationToken的两种采取。

1.前言

方今趁着种类的1段平稳期研读了点不清书本,其中《C#出现编制程序卓越实例》给自身的回忆依旧比较深刻的。当然,那也许是由于近段日子看的书多数嘴炮大于实际,如《Head
First设计方式》《Cracking the coding
interview》等,所以突然见到1本打着“实例”暗记的书本,依旧挺让自家感到耳目壹新。本着分享和加重了然的目标,我特意整理了有个别笔记(首要是Web开辟中易于涉及的剧情,所以有个别章节如数据流,RubiconX等自作者看了看就向来跳过了),以供审阅学习。语言和本领的魔力,真是不得捉摸

1.前言

如今趁着类其他一段平稳期研读了众多书籍,个中《C#并发编制程序杰出实例》给自个儿的印象只怕相比深刻的。当然,那恐怕是出于近段日子看的书大多嘴炮大于实际,如《Head
First设计格局》《Cracking the coding
interview》等,所以突然见到1本打着“实例”暗号的书籍,依然挺让自家以为耳目壹新。本着分享和加深精通的指标,笔者专门整理了1些笔记(首即便Web开辟中轻便涉及的剧情,所以某些章节如数据流,帕杰罗X等自家看了看就一贯跳过了),以供审阅学习。语言和本领的魅力,真是不得捉摸

哪些是异步?

异步处理不用阻塞当前线程来等待处理到位,而是允许继续操作,直至别的线程将处理达成,并回调通知此线程。

一,ThreadPool直接开发银行线程,传递CancellationToken。

二.直截了当

一向以来都有一种观点是落到实处底层架构,编写驱动和引擎,大概是框架和工具开垦的才是高级开辟人士,做上层应用的人只有是“码农”,其实可以利用好平台提供的有关类库,而不是一体使用底层本事和好达成,开拓出高素质,稳固的应用程序,对技能技巧的考验并不低于开辟底层库,如TPL,async,await等。

二.直截了当

直白以来都有1种思想是促成底层架构,编写驱动和引擎,只怕是框架和工具开拓的才是高端开垦人士,做上层应用的人偏偏是“码农”,其实能够利用好平台提供的连带类库,而不是总体采取底层技艺和好达成,开采出高素质,牢固的应用程序,对技巧工夫的考验并不低于开拓底层库,如TPL,async,await等。

异步和多线程

同样点:防止调用线程阻塞,从而加强软件的可响应性。

不同点:

异步操作无须额外的线程负担,并且选拔回调的主意实行拍卖,在规划赏心悦目的动静下,处理函数能够不用接纳共享变量(即便不或许完全不用,最起码可以减小
共享变量的数据),收缩了死锁的恐怕。C#伍.0 .NET肆.伍未来首要字Async和Await的接纳,使得异步编程变得万分轻巧。

八线程中的处理程序依然是逐壹推行,可是多线程的败笔也一律强烈,线程的运用(滥用)会给系统带来上下文切换的额外负担。并且线程间的共享变量也许导致死锁的产出。

贰,Task运营线程,传递CancellationToken。Task传递格局分为两种,壹种通过Task的参数进行传递,另1种通过向线程内传递对象的法门传送CancellationToken。

三.支出条件和要义

三.开拓规范和要义

异步应用场景及原理

线程安全使用,并发编制程序精湛实例。异步首要采用于IO操作,数据库访问,磁盘操作,Socket访问、HTTP/TCP互联网通讯 

亚洲必赢官网,缘由:对于IO操作并不须要CPU进行过多的测算,那些数据首要透过磁盘举行处理,固然进行共同通信无法收场,需求创设更加多的线程能源,线程的数码上下文频仍的切换也是对能源的浪费,针对IO操作不必要单独的分配多少个线程来处理。

举例说明:

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

一齐处理请求的线程会被卡住,异步处理请求的线程不会堵塞。

亚洲必赢官网 1

 

三,CancellationToken的回调函数应用。

(一)并发编制程序概述

  1. 出现:同时做多件业务
  2. 十二线程:并发的壹种格局,它选取多个线程来实行顺序
  3. 并行处理:把正在试行的大批量的任务分割成小块,分配给多少个同时运行的线程
  4. 并行处理是八线程的壹种,而十二线程是出新的壹种处理情势
  5. 异步编制程序:并发的1种情势,它使用future形式只怕callback机制,以制止发出不须求的线程
  6. 异步编程的核心境念是异步操作:运营了的操作会在1段时间后变成。那么些操作正在实行时,不会阻塞原来的线程。运行了这一个操作的线程,能够继续实行其余职责。当操作完毕后,会打招呼它的future,或然调用回调函数,以便让程序知道操作已经竣事
  7. await关键字的成效:运转一个将会被试行的Task(该Task就要新线程中运作),并立刻回到,所以await所在的函数不会被堵塞。当Task完毕后,继续实施await前面包车型客车代码
  8. 响应式编制程序:并发的一种基于评释的编制程序情势,程序在该形式中对事件作出反应
  9. 决不用 void 作为 async 方法的回到类型! async 方法能够回去
    void,不过那仅限于编写事件处理程序。三个常见的 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. 大大多产出编制程序技能都有三个类似点:它们本质上都是函数式的。那里的函数式是作为一种基于函数组合的编制程序方式。函数式的2个编制程序原则是轻巧(防止副成效),另贰个是不改变性(指1段数据不可能被改换)
  18. .NET 4.0
    引进了相互职务库(TPL),完全补助数据交互和天职并行。然则有的能源较少的
    平台(例如手提式有线电话机),常常不辅助 TPL。TPL 是 .NET 框架自带的

(壹)并发编程概述

  1. 出现:同时做多件业务
  2. 四线程:并发的一种情势,它利用几个线程来施行顺序
  3. 并行处理:把正在施行的大气的职责分割成小块,分配给多少个同时运营的线程
  4. 并行处理是四线程的一种,而二十四线程是出新的1种处理方式
  5. 异步编制程序:并发的壹种方式,它采纳future情势只怕callback机制,以制止发生不要求的线程
  6. 异步编程的大旨绪念是异步操作:运行了的操作会在一段时间后形成。那个操作正在实施时,不会阻塞原来的线程。运维了那一个操作的线程,能够继续试行其他职责。当操作完成后,会打招呼它的future,或然调用回调函数,以便让程序知道操作已经终结
  7. await关键字的功效:启动1个将会被实践的Task(该Task将要新线程中运行),并随即回去,所以await所在的函数不会被封堵。当Task实现后,继续实行await前面包车型客车代码
  8. 响应式编程:并发的1种基于注脚的编制程序方式,程序在该形式中对事件作出反应
  9. 无须用 void 作为 async 方法的回到类型! async 方法能够重临void,不过那只限于编写事件处理程序。四个平凡的 async
    方法如若未有重回值,要回到 Task,而不是 void
  10. async 方法在始发时以联合格局实行。在 async 方法内部,await
    关键字对它的参数实行贰个异步等待。它首先检查操作是还是不是业已产生,要是形成了,就此起彼伏运营(同步格局)。不然,它会中断 async 方法,并重返,留下1个未成功的
    task。一段时间后, 操作完毕,async
    艺术就过来运转。
  11. await代码中抛出尤其后,分外会沿着Task方向前进到引用处
  12. 您只要在代码中使用了异步,最佳从来使用。调用
    异步方法时,应该(在调用截至时)用 await 等待它回到的 task
    对象。一定要幸免使用 Task.Wait 或 Task.Result
    方法,因为它们会导致死锁
  13. 线程是四个独自的运行单元,每一种进度之中有四个线程,每一个线程能够分别同时施行命令。
    每一种线程有友好独立的栈,可是与经过内的任何线程共享内部存款和储蓄器
  14. 各样.NET应用程序都维护着二个线程池,那种情形下,应用程序大概不需求活动成立新的线程。你若要为
    COM interop 程序创制 SAT 线程,就得 创制线程,那是唯壹须要线程的情事
  15. 线程是低等其他画饼充饥,线程池是多少高档一点的空洞
  16. 并发编程用到的聚集有两类:并发产生+不可变集合
  17. 大很多现身编制程序技巧都有二个类似点:它们本质上都是函数式的。那里的函数式是作为一种基于函数组合的编制程序情势。函数式的贰个编制程序原则是精简(制止副成效),另二个是不改变性(指1段数据不能够被退换)
  18. .NET 四.0
    引进了相互任务库(TPL),完全辅助数据交互和天职并行。可是有个别能源较少的
    平台(例如手提式有线电电话机),日常不援助 TPL。TPL 是 .NET 框架自带的

任务

  在使用职分此前,针对线程的调用多数都用线程池提供的静态方法QueueUserWorkItem,不过那几个函数有广大的范围,个中最大的主题材料即便从未内部机制能够让开垦者知道操作在怎么时候做到,也从未机制在操作完毕时获得重临值,微软为了化解那个题目引进了任务的概念。

 首先构造2个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对象完毕。

话不多说,请看代码。

(二)异步编制程序基础

  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. 怎么着实现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和上下文的主题材料:在暗许情形下,2个 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 方法成立并再次来到3个新的 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的专业裁撤操作格局,首先供给创立多少个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!"   );
        }
    }

(3)并行开采的底子

  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 类有3个轻易的积极分子
    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 另七个职务,建立一种隐 式的“父 /
    子”关系。

任务到位后自行运行新职务

事实上的支出应用中,日常出现3遍职务到位后即刻运行其它1个任务,并且不可见使线程阻塞,在职责未能如愿时调用result会使程序阻塞,不能查看职责的实行进程,TASK提供了一个格局ContinueWith,它不会阻塞任何线程,当第二个职务成功时,会及时运维第三个任务。

 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 Studio二零一三 版本起初扶助 async Task 类型的单元测试
  2. 假诺单元测试框架不帮衬 async Task
    类型的单元测试,就需求做壹些13分的改造技巧等待异步操作。个中壹种做法是利用
    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 Studio2013 版本开始扶助 async Task 类型的单元测试
  2. 如若单元测试框架不帮忙 async Task
    类型的单元测试,就需求做一些外加的改变本领等待异步操作。个中一种做法是行使
    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。

Async&Await 轻易利用

利用Async&Await的要害目标是有益开始展览异步操作,因为.net
四.0
在此以前举办异步操作时比较复杂的,重假如通过调用微软提供的异步回调方法开始展览编程,借使遭遇需求协调完结的法子显得格外头痛,.net的逐条版本都有投机首选的工夫,像.NET一.第11中学的委托,.NET二.0中的泛型,.NET三.0中的Linq,.NET四.0中的Dynimac,.net四.5首推的就是异步编制程序,大家只供给精晓TASK+异步函数就可以完毕异步编制程序。

async:告诉CL汉兰达那是3个异步函数。

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代码,那是因为异步开启了新的线程,并不会促成线程阻塞。

 

亚洲必赢官网 2

(5)集合

  1. 线程安全集合是可同时被五个线程修改的可变集合。线程安全集合混合使用了细粒度锁定和无锁技巧,以担保线程被卡住的年月最短(日常情形下是一向不打断)。对广大线程安全集合实行枚举操作时,内部成立了该集合的一个快照(snapshot),并对那么些快照实行枚举操作。线程安全集合的根本优点是八个线程能够安全地对其进展走访,而代码只会被打断十分的短的光阴,或根本不打断

  2. ConcurrentDictionary是数据结构中的精品,它是线程安全的,混合使用了细粒度锁定和无锁本领,以保险大多数意况下能开始展览高效访问.

  3. ConcurrentDictionary 内置了AddOrUpdate, TryRemove,
    TryGetValue等艺术。假诺多少个线程读写三个共享集合,使用ConcurrentDictionary是最合适的,假如不会反复修改,这就更适合采用ImmutableDictionary。而假使是局地线程只添比索素,壹些线程只移除成分,最佳使用生产者/消费者集合

(5)集合

  1. 线程安全集合是可同时被多个线程修改的可变集合。线程安全集合混合使用了细粒度锁定和无锁工夫,以保障线程被堵塞的年月最短(平时状态下是一向不封堵)。对很二十多线程安全集合进行枚举操作时,内部成立了该集合的三个快速照相(snapshot),并对那一个快速照相进行枚举操作。线程安全集合的重点优点是多少个线程能够安枕无忧地对其开始展览访问,而代码只会被封堵不够长的光阴,或根本不封堵

  2. ConcurrentDictionary<TKey,
    电视机alue>是数据结构中的精品,它是线程安全的,混合使用了细粒度锁定和无锁本事,以保证大多数气象下能张开高效访问.

  3. ConcurrentDictionary<TKey, 电视机alue> 内置了AddOrUpdate,
    TryRemove,
    TryGetValue等艺术。如若多少个线程读写1个共享集合,使用ConcurrentDictionary<TKey,
    TValue>是最合适的,假若不会频仍修改,那就更符合采纳ImmutableDictionary<TKey,
    TValue>。而只借使有的线程只添澳成分,一些线程只移除成分,最佳使用生产者/消费者集合

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

(6)函数式OOP

  1. 异步编制程序是函数式的(functional),.NET
    引进的async让开辟者举行异步编制程序的时候也能用进程式编制程序的思维来开始展览思量,但是在中间贯彻上,异步编程还是是函数式的

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

  2. 能够用await等待的是1个类(如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对象),而不是一个艺术。可以用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篇小说是20壹3年,就算历时近5年,但请相信自个儿,代码早在伍年前就早已写完呀。只是小编直接一贯一贯没配文字发出来。。。。。。

(7)同步

  1. 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;
}

本例中,运维了八个并发运营的修改进度。供给1块啊?答案是“看状态”。尽管能鲜明这么些方法是在 GUI 或 ASP.NET
上下文中调用的(或同一时间内只同意壹段代码运维的任
何其余上下文),那就不要求一块,因为那八个修改数据经过的运维时刻是互分歧样的。
例如,假诺它在 GUI 上下文中运作,就唯有二个 UI
线程能够运营那些数量修改进程,由此一段时间内只好运维三个历程。因而,即使能够规定是“同一时半刻间只运转一段代码”的
上下文,那就不供给一同。然而如果从线程池线程(如
Task.Run)调用那一个办法,就须求1块了。在那种情景下,那多少个数据修改进程会在单独的线程池线程中运作,并且还要修改
data.Value,因而必须联合地走访 data.Value。

  1. 不可变类型自个儿就是线程安全的,修改一个不可变集合是不大概的,就算使用八个Task.Run向聚集中添增添少,也并不要求同步操作

  2. 线程安全集合(例如
    ConcurrentDictionary)就全盘两样了。与不可变集合分裂,线程安
    全集合是能够修改的。线程安全集合本身就包括了具备的一路功效

  3. 至于锁的使用,有肆条至关首要的清规戒律

  • 界定锁的功用范围(例如把lock语句使用的对象设为私有成员)
  • 文书档案中写清锁的功力内容
  • 锁范围内的代码尽量少(锁定期不用进行围堵操作)
  • 在调节锁的时候不要运维随意的代码(不要在言辞中调用事件处理,调用虚拟方法,调用委托)
  1. 假设急需异步锁,请尝试 塞马phoreSlim

  2. 永不在 ASP. NET 中央银行使 Task.Run,那是因为在 ASP.NET
    中,处理请求的代码本来便是在线程池线程中运作的,强行把它内置另2个线程池线程通常会大失所望

(7) 实用手艺

  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. 着眼以下代码,明确其共同和周转境况

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 上下文中运维,就只有一个 UI
线程能够运作这一个多少修改进程,因此一段时间内只好运营一个经过。因而,就算能够分明是“同一时半刻间只运营壹段代码”的
上下文,那就不须要共同。然而借使从线程池线程(如
Task.Run)调用这么些措施,就要求联合了。在那种意况下,那四个数据修改进程会在独立的线程池线程中运作,并且同时修改
data.Value,由此必须一同地访问 data.Value。

  1. 不足变类型本身就是线程安全的,修改四个不可变集合是不或然的,即使使用四个Task.Run向聚集中添增添少,也并不须求同步操作

  2. 线程安全集合(例如
    ConcurrentDictionary)就完全两样了。与不可变集合分歧,线程安
    全集合是能够修改的。线程安全集合本人就含有了具有的一路功效

  3. 关于锁的施用,有肆条首要的规则

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

  2. 不用在 ASP. NET 中使用 Task.Run,那是因为在 ASP.NET
    中,处理请求的代码本来就是在线程池线程中运维的,强行把它内置另三个线程池线程常常会弄巧成拙

(7) 实用技术

  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;
}

不过,也恐怕是方今写文字的力量有着晋级,所以就到位了4和伍。

不然那线程安全的篇章也许还要拖。。。。。。。。哈哈

 后记

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

 


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

网站地图xml地图