威尼斯人线上娱乐

线程安全采用,异步编制程序

17 4月 , 2019  

 CancellationToken的有余应用

  • 1.前言
  • 2.直言不讳
  • 三.支出条件和大旨
    • (壹)并发编制程序概述
    • (贰)异步编制程序基础
    • (叁)并行开垦的基本功
    • (④)测试本领
    • (5)集合
    • (6)函数式OOP
    • (7)同步
  • 1.前言
  • 2.开门见山
  • 三.支付条件和宗旨
    • (一)并发编制程序概述
    • (2)异步编制程序基础
    • (三)并行开拓的功底
    • (4)测试技巧
    • (5)集合
    • (6)函数式OOP
    • (7)同步

前言

本节重视介绍异步编制程序中Task、Async和Await的基础知识。

那是线程安全的结尾一篇了,首要介绍CancellationToken的有余运用。

1.前言

近年来趁着品种的壹段平稳期研读了众多图书,在那之中《C#出现编制程序优异实例》给自身的记念依旧比较深刻的。当然,那只怕是由于近段日子看的书诸多嘴炮大于实际,如《Head
First设计方式》《Cracking the coding
interview》等,所以突然见到1本打着“实例”记号的书籍,如故挺让作者以为耳目1新。本着分享和强化驾驭的目标,作者专门整理了有的笔记(首固然Web开采中易于涉及的剧情,所以某个章节如数据流,牧马人X等本身看了看就一直跳过了),以供审阅学习。语言和技巧的魔力,真是不得捉摸

1.前言

新近趁着类别的1段平稳期研读了众多书本,当中《C#并发编制程序杰出实例》给自家的影像恐怕比较长远的。当然,那只怕是出于近段日子看的书繁多嘴炮大于实际,如《Head
First设计方式》《Cracking the coding
interview》等,所以突然见到一本打着“实例”暗号的书本,依旧挺让本人感觉耳目1新。本着分享和深化通晓的指标,我特意整理了有的笔记(主若是Web开荒中轻巧涉及的剧情,所以有的章节如数据流,LacrosseX等本人看了看就直接跳过了),以供审阅学习。语言和手艺的魔力,真是不得捉摸

什么样是异步?

异步处理不用阻塞当前线程来等待处理完了,而是允许继续操作,直至其余线程将拍卖到位,并回调布告此线程。

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

2.直截了当

平素以来都有1种意见是落到实处底层架构,编写驱动和电动机,或然是框架和工具开荒的才是高端开辟人士,做上层应用的人只有是“码农”,其实可以接纳好平台提供的相干类库,而不是1体行使底层手艺和好达成,开荒出高水平,稳固的应用程序,对本领本事的考验并不低于开垦底层库,如TPL,async,await等。

二.直截了当

直接以来都有1种意见是完成底层架构,编写驱动和内燃机,恐怕是框架和工具开拓的才是尖端开垦职员,做上层应用的人独自是“码农”,其实可以使用好平台提供的连锁类库,而不是任何运用底层技能和好落成,开荒出高水平,稳定的应用程序,对技技艺量的考验并非常大于开辟底层库,如TPL,async,await等。

异步和十二线程

同样点:制止调用线程阻塞,从而巩固软件的可响应性。

不同点:

异步操作无须额外的线程负担,并且应用回调的方式张开拍卖,在统一筹划能够的情事下,处理函数能够无需选拔共享变量(固然不可能完全不用,最起码能够减去
共享变量的多寡),收缩了死锁的只怕。C#⑤.0 .NET四.5将来首要字Async和Await的接纳,使得异步编制程序变得不行轻便。

二十多线程中的处理程序依旧是逐一实行,可是多线程的败笔也一致引人侧目,线程的利用(滥用)会给系统带来上下文切换的额外负担。并且线程间的共享变量恐怕产生死锁的面世。

贰,Task运营线程,传递CancellationToken。Task传递格局分为二种,1种通过Task的参数实行传递,另1种通过向线程内传递对象的点子传递CancellationToken。

威尼斯人线上娱乐 ,3.支出条件和中央

3.开垦规范和大旨

异步应用场景及原理

异步首要利用于IO操作,数据库访问,磁盘操作,Socket访问、HTTP/TCP互连网通讯 

案由:对于IO操作并不必要CPU实行过多的估算,这几个数据主要透过磁盘进行处理,假若进行协同通讯不恐怕收场,供给创造越来越多的线程财富,线程的多寡上下文频繁的切换也是对财富的浪费,针对IO操作不要求单独的抽成一个线程来拍卖。

举例表明:

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

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

威尼斯人线上娱乐 1

 

线程安全采用,异步编制程序。三,CancellationToken的回调函数应用。

(一)并发编制程序概述

  1. 并发:同时做多件业务
  2. 拾贰线程:并发的一种样式,它应用三个线程来实施顺序
  3. 并行处理:把正在实施的大方的职务分割成小块,分配给多少个同时运转的线程
  4. 并行处理是十二线程的1种,而二十二十四线程是出现的一种处理格局
  5. 异步编制程序:并发的壹种方式,它使用future方式恐怕callback机制,以制止暴发不必要的线程
  6. 异步编制程序的核激情念是异步操作:运行了的操作会在1段时间后成功。这么些操作正在施行时,不会卡住原来的线程。运维了那么些操作的线程,能够继续实施其余任务。当操作达成后,会打招呼它的future,恐怕调用回调函数,以便让程序知道操作已经结束
  7. await关键字的魔法:运行三个将会被实践的Task(该Task将在新线程中运转),并及时返回,所以await所在的函数不会被打断。当Task完成后,继续施行await前面包车型大巴代码
  8. 响应式编制程序:并发的一种基于评释的编制程序情势,程序在该情势中对事件作出反应
  9. 永不用 void 作为 async 方法的回到类型! async 方法能够重回void,不过这只限于编写事件处理程序。2个平凡的 async
    方法如若没有重临值,要回到 Task,而不是 void
  10. async 方法在上兔时以共同格局执行。在 async 方法内部,await
    关键字对它的参数试行三个异步等待。它首先检查操作是或不是早已实现,假使产生了,就三番五次运转(同步情势)。不然,它会中断 async 方法,并再次回到,留下一个未到位的
    task。壹段时间后, 操作实现,async
    主意就死灰复燃运营。
  11. await代码中抛出相当后,非凡会沿着Task方向前进到引用处
  12. 您若是在代码中央银行使了异步,最棒一贯利用。调用
    异步方法时,应该(在调用甘休时)用 await 等待它回到的 task
    对象。一定要防止采取 Task.Wait 或 Task.Result
    方法,因为它们会促成死锁
  13. 线程是贰个单独的运作单元,每一种进度之中有多个线程,各个线程能够分别同时进行命令。
    每一个线程有温馨单独的栈,不过与经过内的别的线程共享内部存款和储蓄器
  14. 各种.NET应用程序都维护着一个线程池,那种情形下,应用程序大约不须要活动成立新的线程。你若要为
    COM interop 程序创设 SAT 线程,就得 创设线程,那是绝无仅有必要线程的情状
  15. 线程是低档别的虚幻,线程池是有些高档一点的抽象
  16. 并发编制程序用到的成团有两类:并发形成+不可变集合
  17. 绝大很多油然则生编制程序工夫都有2个类似点:它们本质上都以函数式的。那里的函数式是当做壹种基于函数组合的编制程序方式。函数式的2个编制程序原则是简单(制止副成效),另2个是不改变性(指一段数据无法被修改)
  18. .NET 肆.0
    引进了互相职务库(TPL),完全援助数据交互和天职并行。可是有的能源较少的
    平台(例如手提式有线电话机),日常不协理 TPL。TPL 是 .NET 框架自带的

(壹)并发编制程序概述

  1. 并发:同时做多件工作
  2. 八线程:并发的壹种情势,它使用八个线程来举办顺序
  3. 并行处理:把正在推行的多量的职责分割成小块,分配给八个同时运营的线程
  4. 并行处理是二十四线程的1种,而四线程是现身的壹种处理格局
  5. 异步编制程序:并发的1种样式,它使用future情势大概callback机制,防止止爆发不供给的线程
  6. 异步编制程序的核心思念是异步操作:运转了的操作会在1段时间后成功。这一个操作正在执行时,不会卡住原来的线程。运维了这几个操作的线程,能够继续实践别的义务。当操作完毕后,会通报它的future,也许调用回调函数,以便让程序知道操作已经停止
  7. await关键字的意义:运维三个将会被实践的Task(该Task将在新线程中运作),并及时回到,所以await所在的函数不会被堵塞。当Task完结后,继续施行await前面包车型大巴代码
  8. 响应式编制程序:并发的1种基于表明的编制程序方式,程序在该格局中对事件作出反应
  9. 毫不用 void 作为 async 方法的回来类型! async 方法能够重临void,但是那只限于编写事件处理程序。一个普通的 async
    方法假使未有重临值,要回到 Task,而不是 void
  10. async 方法在开始时以协同格局实施。在 async 方法内部,await
    关键字对它的参数推行1个异步等待。它首先检查操作是不是已经成功,假使做到了,就连任运维(同步情势)。不然,它会停顿 async 方法,并赶回,留下3个未到位的
    task。1段时间后, 操作实现,async
    主意就恢复生机械运输维。
  11. await代码中抛出非凡后,卓殊会顺着Task方向前进到引用处
  12. 你一旦在代码中选拔了异步,最棒平素使用。调用
    异步方法时,应该(在调用甘休时)用 await 等待它回到的 task
    对象。一定要制止采用 Task.Wait 或 Task.Result
    方法,因为它们会形成死锁
  13. 线程是1个单身的运行单元,各个进度之中有多个线程,每个线程可以独家同时奉行命令。
    每一种线程有投机单身的栈,可是与经过内的别的线程共享内部存款和储蓄器
  14. 各种.NET应用程序都维护着二个线程池,这种情状下,应用程序差不离不须求活动创制新的线程。你若要为
    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对象实现。

话不多说,请看代码。

(二)异步编制程序基础

  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. 怎么促成3个有所异步签字的联手方法。借使从异步接口或基类传承代码,但期待用协同的主意来贯彻它,就会师世那种气象。解决办法是能够动用
    Task.FromResult 方法成立并赶回贰个新的 Task 对象,那几个 Task
    对象是早已 完成的,并有钦点的值
  3. 行使 IProgress 和 Progress 类型。编写的 async 方法必要有 IProgress
    参数,其 中 T 是亟需报告的快慢类型,能够突显操作的快慢
  4. Task.WhenALl能够等待全体任务达成,而当种种Task抛出非凡时,能够选拔性捕获相当
  5. Task.WhenAny可以等待任1义务实现,使用它就算能够形成超时任务(在这之中一个Task设为Task.Delay),可是鲜明用专门的带有撤除标记的过期函数处理比较好
  6. 第一章提到async和上下文的难题:在暗中认可景况下,一个 async 方法在被
    await
    调用后复苏运维时,会在原来的上下文中运作。而丰裕扩张方法ConfigureAwait(false)后,则会在await之后遗弃上下文

(贰)异步编制程序基础

  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抛出10分时,能够接纳性捕获分外
  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!"   );
        }
    }

(三)并行开辟的根底

  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 另一个职务,建立一种隐 式的“父 /
    子”关系。

(3)并行开垦的基础

  1. Parallel 类有2个轻易的积极分子
    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回职分到位后立马运行其它3个职分,并且不可能使线程阻塞,在职分未遂时调用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 Studio2011 版本最先支持 async Task 类型的单元测试
  2. 假如单元测试框架不援助 async Task
    类型的单元测试,就须求做壹些相当的修改才具等待异步操作。在那之中壹种做法是利用
    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。

(4)测试技巧

  1. MSTest从Visual Studio二〇一一 版本初步扶助 async Task 类型的单元测试
  2. 设若单元测试框架不帮忙 async Task
    类型的单元测试,就供给做壹些附加的修改技艺等待异步操作。个中一种做法是应用
    Task.Wait,并在有不当时拆开 AggregateException 对象。作者的提出是采取NuGet 包 Nito.AsyncEx 中的 AsyncContext 类

那里附上2个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本田CR-V那是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。而假诺是有个别线程只添比索素,1些线程只移除成分,最棒使用生产者/消费者集合

(5)集合

  1. 线程安全集合是可同时被多少个线程修改的可变集合。线程安全集合混合使用了细粒度锁定和无锁技能,以管教线程被封堵的日子最短(平常状态下是根本不封堵)。对广大线程安全集合举办枚举操作时,内部创设了该集合的1个快速照相(snapshot),并对这么些快速照相举办枚举操作。线程安全集合的要害优点是五个线程能够安枕无忧地对其张开访问,而代码只会被堵塞相当短的岁月,或根本不封堵

  2. ConcurrentDictionary<TKey,
    电视机alue>是数据结构中的精品,它是线程安全的,混合使用了细粒度锁定和无锁手艺,以担保超越1/2情形下能开始展览高效访问.

  3. ConcurrentDictionary<TKey, 电视机alue> 内置了AddOrUpdate,
    TryRemove,
    TryGetValue等艺术。假诺四个线程读写贰个共享集合,使用ConcurrentDictionary<TKey,
    TValue>是最合适的,若是不会反复修改,那就更符合选择ImmutableDictionary<TKey,
    电视机alue>。而假使是部分线程只添欧成分,壹些线程只移除成分,最佳使用生产者/消费者集合

到此NET Framework4.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. 类的构造函数里是无法举行异步操作的,1般能够使用如下方法。相应的,大家得以经过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. 在编写异步事件处理器时,事件参数类最棒是线程安全的。要做到这一点,最轻易易行的办法正是让它成为不可变的(即把具备的质量都设为只读)

虽说第一篇小说是201三年,固然历时近5年,但请相信作者,代码早在五年前就曾经写完啦。只是笔者直接从来从来没配文字发出来。。。。。。

(7)同步

  1. 共同的品种首要有二种:通讯和数据珍惜

  2. 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 上下文中运作,就只有贰个 UI
线程能够运作这么些数量修改进度,因而一段时间内只能运转一个进度。由此,若是能够规定是“同如今间只运营一段代码”的
上下文,那就不须要壹块。可是只要从线程池线程(如
Task.Run)调用这几个主意,就供给一齐了。在那种情景下,那三个数据修改进度会在单独的线程池线程中运作,并且还要修改
data.Value,因而必须共同地走访 data.Value。

  1. 不可变类型自身正是线程安全的,修改2个不可变集合是不容许的,尽管使用七个Task.Run向聚集中添扩张少,也并不需求同步操作

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

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

  • 界定锁的遵循范围(例如把lock语句使用的靶子设为私有成员)
  • 文书档案中写清锁的效应内容
  • 锁范围内的代码尽量少(锁按期不要开始展览围堵操作)
  • 在调整锁的时候绝不运维随意的代码(不要在讲话中调用事件处理,调用虚拟方法,调用委托)
  1. 假设急需异步锁,请尝试 SemaphoreSlim

  2. 决不在 ASP. NET 中运用 Task.Run,那是因为在 ASP.NET
    中,处理请求的代码本来正是在线程池线程中运作的,强行把它内置另三个线程池线程平时会白璧微瑕

(七) 实用技能

  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
上下文中调用的(或同临时间内只同意1段代码运营的任
何别的上下文),那就不需求联合,因为那四个修改数据经过的运维时刻是互分化的。
例如,假如它在 GUI 上下文中运作,就唯有贰个 UI
线程能够运维那一个数量修改进度,因而1段时间内只好运维1个进度。由此,要是可以规定是“同权且间只运营1段代码”的
上下文,那就不必要联合。不过如果从线程池线程(如
Task.Run)调用那么些方式,就须求共同了。在那种意况下,那八个数据修改进度会在独立的线程池线程中运维,并且同时修改
data.Value,由此必须同步地访问 data.Value。

  1. 不行变类型本人正是线程安全的,修改2个不可变集合是不容许的,即使使用多少个Task.Run向聚集中添增加少,也并不须求同步操作

  2. 线程安全集合(例如
    ConcurrentDictionary)就完全两样了。与不可变集合分歧,线程安
    全集合是能够修改的。线程安全集合本人就包蕴了有着的联合成效

  3. 关于锁的应用,有4条至关心珍视要的清规戒律

  • 界定锁的职能范围(例如把lock语句使用的目的设为私有成员)
  • 文书档案中写清锁的功力内容
  • 锁范围内的代码尽量少(锁按期绝不实行围堵操作)
  • 在调控锁的时候不要运营随意的代码(不要在说话中调用事件处理,调用虚拟方法,调用委托)
  1. 假定供给异步锁,请尝试 SemaphoreSlim

  2. 毫无在 ASP. NET 中选择 Task.Run,那是因为在 ASP.NET
    中,处理请求的代码本来就是在线程池线程中运作的,强行把它内置另3个线程池线程常常会白璧微瑕

(7) 实用手艺

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

可是,也或许是方今写文字的力量有着进级,所以就到位了肆和五。

不然那线程安全的稿子也许还要拖。。。。。。。。哈哈

 后记

在NET
Framework四.陆里,微软提供了async和await语法,也是关于线程安全,笔者将会在新的语法相关文章里上课async和await的用法。

 


注:此作品为原创,欢迎转载,请在小说页面显然地方给出此文链接!
若您感到那篇小说还不错请点击下右下角的推荐,非凡感激!


相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图