线程也疯狂,并发编制程序优秀实例

 CancellationToken的有余施用

  • 1.前言
  • 二.直截了当
  • 三.付出原则和要义
    • (一)并发编制程序概述
    • (贰)异步编制程序基础
    • (3)并行开采的根底
    • (四)测试技艺
    • (5)集合
    • (6)函数式OOP
    • (7)同步
  • 1.前言
  • 二.开门见山
  • 三.支出条件和中央
    • (一)并发编制程序概述
    • (2)异步编制程序基础
    • (三)并行开辟的基本功
    • (肆)测试工夫
    • (5)集合
    • (6)函数式OOP
    • (7)同步

前言

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

那是线程安全的最终一篇了,首要介绍CancellationToken的五种行使。

1.前言

最近趁着体系的1段平稳期研读了过多书本,个中《C#出现编制程序非凡实例》给本身的影象或然比较深远的。当然,那或然是由于近段日子看的书很多嘴炮大于实际,如《Head
First设计格局》《Cracking the coding
interview》等,所以突然见到一本打着“实例”暗记的书籍,依然挺让自家认为耳目一新。本着分享和加深领悟的目标,小编专门整理了某个笔记(首借使Web开荒中轻松涉及的剧情,所以某些章节如数据流,RAV4X等小编看了看就平昔跳过了),以供审阅学习。语言和本领的魔力,真是不得捉摸

1.前言

如今趁着品种的1段平稳期研读了大多书籍,当中《C#并发编制程序精粹实例》给本身的印象恐怕比较深入的。当然,那也许是出于近段日子看的书许多嘴炮大于实际,如《Head
First设计方式》《Cracking the coding
interview》等,所以突然见到1本打着“实例”暗记的图书,依然挺让本身认为耳目1新。本着分享和加深明白的指标,小编越发整理了部分笔记(首假若Web开垦中易于涉及的内容,所以部分章节如数据流,大切诺基X等自个儿看了看就径直跳过了),以供审阅学习。语言和手艺的魅力,真是不得捉摸

何以是异步?

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

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

2.开宗明义

直接以来都有一种意见是落到实处底层架构,编写驱动和斯特林发动机,也许是框架和工具开垦的才是尖端开拓人士,做上层应用的人唯有是“码农”,其实能够使用好平台提供的相干类库,而不是整整利用底层技能和好达成,开荒出高水平,牢固的应用程序,对本事力量的考验并非常大于开拓底层库,如TPL,async,await等。

二.开门见山

一向以来都有一种意见是促成底层架构,编写驱动和引擎,或然是框架和工具开垦的才是高级开拓人员,做上层应用的人单纯是“码农”,其实能够使用好平台提供的有关类库,而不是整套使用底层技艺和好达成,开采出高水平,牢固的应用程序,对本事本事的考验并不小于开采底层库,如TPL,async,await等。

异步和十二线程

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

不同点:

异步操作无须额外的线程负担,并且动用回调的点子张开处理,在规划美丽的境况下,处理函数能够不必接纳共享变量(纵然无法完全不用,最起码能够减小
共享变量的数额),减弱了死锁的可能。C#伍.0 .NET四.5未来首要字Async和Await的行使,使得异步编制程序变得至极简单。

八线程中的处理程序依旧是各类实行,然则多线程的缺点也如出壹辙显著,线程的施用(滥用)会给系统带来上下文切换的额外负担。并且线程间的共享变量恐怕变成死锁的面世。

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

三.支出条件和大旨

三.付出规范和中央

异步应用场景及原理

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

原因:对于IO操作并不要求CPU进行过多的计量,这个多少重要通过磁盘举行拍卖,假若张开同步通讯不可能收场,需求成立更多的线程能源,线程的数据上下文频仍的切换也是对能源的荒废,针对IO操作不要求独自的分红1个线程来拍卖。

举例表达:

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

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

美高梅开户网址 1

 

线程也疯狂,并发编制程序优秀实例。叁,CancellationToken的回调函数应用。

(一)并发编制程序概述

  1. 并发:同时做多件工作
  2. 10二线程:并发的一种样式,它选取三个线程来进行顺序
  3. 并行处理:把正在试行的大度的天任务割成小块,分配给多个同时运转的线程
  4. 并行处理是十2线程的壹种,而二十10贰线程是现身的壹种处理方式
  5. 异步编制程序:并发的壹种情势,它应用future方式大概callback机制,以避免发出不须求的线程
  6. 异步编制程序的核激情念是异步操作:启动了的操作会在壹段时间后产生。这些操作正在实行时,不会阻塞原来的线程。运维了这么些操作的线程,能够继续推行其余职务。当操作完毕后,会打招呼它的future,可能调用回调函数,以便让程序知道操作已经终止
  7. await关键字的效果:运转3个将会被实施的Task(该Task将要新线程中运转),并立即回到,所以await所在的函数不会被堵塞。当Task完毕后,继续推行await前边的代码
  8. 响应式编制程序:并发的壹种基于注解的编程方式,程序在该情势中对事件作出反应
  9. 永不用 void 作为 async 方法的回到类型! async 方法可以回去
    void,可是那只限于编写事件处理程序。1个平淡无奇的 async
    方法假使未有重临值,要回去 Task,而不是 void
  10. async 方法在开首时以协同格局执行。在 async 方法内部,await
    关键字对它的参数实施三个异步等待。它首先检查操作是还是不是已经产生,假设产生了,就持续运转(同步格局)。不然,它会搁浅 async 方法,并再次来到,留下五个未到位的
    task。1段时间后, 操作完毕,async
    艺术就过来运营。
  11. await代码中抛出非常后,相当会沿着Task方向前进到引用处
  12. 您若是在代码中央银行使了异步,最棒一贯使用。调用
    异步方法时,应该(在调用截止时)用 await 等待它回到的 task
    对象。一定要防止采纳 Task.Wait 或 Task.Result
    方法,因为它们会招致死锁
  13. 线程是二个单独的运转单元,每种进度之中有两个线程,种种线程能够分别同时施行命令。
    每一个线程有自身独自的栈,不过与经过内的其它线程共享内部存款和储蓄器
  14. 各种.NET应用程序都维护着贰个线程池,那种场所下,应用程序大致不供给活动创设新的线程。你若要为
    COM interop 程序创造 SAT 线程,就得 成立线程,那是唯壹供给线程的景色
  15. 线程是低端别的悬空,线程池是有个别高等一点的架空
  16. 并发编制程序用到的聚合有两类:并发形成+不可变集合
  17. 大许多涌出编制程序本事都有2个类似点:它们本质上都以函数式的。这里的函数式是当做壹种基于函数组合的编制程序形式。函数式的三个编制程序原则是精简(幸免副成效),另一个是不改变性(指一段数据不可能被修改)
  18. .NET 四.0
    引进了交互职务库(TPL),完全帮忙数据交互和职分并行。可是有些能源较少的
    平台(例如手提式有线电话机),日常不协理 TPL。TPL 是 .NET 框架自带的

(一)并发编程概述

  1. 并发:同时做多件事业
  2. 二十多线程:并发的1种样式,它利用三个线程来推行顺序
  3. 并行处理:把正在执行的大方的天任务割成小块,分配给七个同时运转的线程
  4. 并行处理是八线程的一种,而四线程是出现的壹种处理情势
  5. 异步编制程序:并发的1种样式,它使用future情势或许callback机制,以免止产生不要求的线程
  6. 异步编制程序的宗旨观念是异步操作:运营了的操作会在一段时间后成功。那个操作正在实践时,不会卡住原来的线程。运行了那么些操作的线程,能够继续实践别的职务。当操作完毕后,会通报它的future,或然调用回调函数,以便让程序知道操作已经完工
  7. await关键字的成效:运转二个将会被实行的Task(该Task将要新线程中运维),并立时重临,所以await所在的函数不会被打断。当Task达成后,继续试行await后边的代码
  8. 响应式编制程序:并发的壹种基于注解的编制程序格局,程序在该方式中对事件作出反应
  9. 毫无用 void 作为 async 方法的回来类型! async 方法能够回到
    void,但是那只限于编写事件处理程序。1个平时的 async
    方法尽管没有重临值,要再次回到 Task,而不是 void
  10. async 方法在始发时以联合方式实践。在 async 方法内部,await
    关键字对它的参数实行几个异步等待。它首先检查操作是还是不是业已形成,假诺变成了,就此起彼伏运转(同步格局)。否则,它会中断 async 方法,并再次来到,留下1个未成功的
    task。一段时间后, 操作实现,async
    艺术就过来运营。
  11. await代码中抛出越发后,分外会顺着Task方向前进到引用处
  12. 您只要在代码中选拔了异步,最棒一直使用。调用
    异步方法时,应该(在调用停止时)用 await 等待它回到的 task
    对象。一定要避免接纳 Task.Wait 或 Task.Result
    方法,因为它们会产生死锁
  13. 线程是1个独自的周转单元,每个进度之中有四个线程,每种线程能够分级同时实施命令。
    每一个线程有温馨单独的栈,不过与经过内的此外线程共享内部存款和储蓄器
  14. 各样.NET应用程序都维护着三个线程池,那种景观下,应用程序大致不要求活动成立新的线程。你若要为
    COM interop 程序创制 SAT 线程,就得 创立线程,这是绝无仅有要求线程的情状
  15. 线程是低等其他肤浅,线程池是有个别高等一点的肤浅
  16. 并发编制程序用到的聚集有两类:并发形成+不可变集合
  17. 超越四6%并发编制程序技能都有2个类似点:它们本质上都是函数式的。这里的函数式是当做一种基于函数组合的编制程序方式。函数式的三个编制程序原则是精简(制止副功能),另二个是不变性(指一段数据不能够被涂改)
  18. .NET 4.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. 指数退避是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. 如何贯彻三个富有异步签名的1道方法。假若从异步接口或基类承继代码,但愿意用联合的法子来促成它,就会油可是生那种景色。化解办法是足以选用Task.FromResult 方法成立并赶回2个新的 Task 对象,这么些 Task
    对象是现已 实现的,并有钦点的值
  3. 行使 IProgress 和 Progress 类型。编写的 async 方法要求有 IProgress
    参数,其 中 T 是急需报告的进度类型,能够展示操作的速度
  4. Task.WhenALl能够等待全体义务达成,而当每一个Task抛出非凡时,能够接纳性捕获格外
  5. Task.WhenAny可以等待任一任务到位,使用它即便能够成功超时职责(当中一个Task设为Task.Delay),然则显著用尤其的隐含打消标识的超时函数处理相比好
  6. 先是章提到async和上下文的标题:在暗中认可情形下,1个 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 方法成立并重返2个新的 Task 对象,那几个 Task
    对象是曾经 完结的,并有钦定的值
  3. 选拔 IProgress 和 Progress 类型。编写的 async 方法供给有 IProgress
    参数,其 中 T 是急需报告的快慢类型,能够来得操作的进度
  4. Task.WhenALl能够等待全体任务到位,而当各样Task抛出越发时,能够选用性捕获格外
  5. Task.WhenAny能够等待任壹任务成功,使用它尽管能够做到超时任务(在那之中多个Task设为Task.Delay),不过鲜明用特其他涵盖取消标识的逾期函数处理比较好
  6. 第3章提到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!"   );
        }
    }

(3)并行开采的根基

  1. Parallel 类有三个简约的成员
    Invoke,可用来须求并行调用一堆措施,并且那么些形式(领先5分之三)是互为独立的

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,它不会堵塞任何线程,当第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的回调函数应用:

(4)测试技巧

  1. MSTest从Visual Studio二〇一三 版本伊始帮助 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 代码中,关键准则之一正是防止使用 async
    void。小编卓殊提出大家在对 async void
    方法做单元测试时张开代码重构,而不是行使 AsyncContext。

(四)测试手艺

  1. MSTest从Visual Studio二零一二 版本开头协理 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 代码中,关键准则之壹正是制止使用 async
    void。作者分外提出我们在对 async void
    方法做单元测试时开展代码重构,而不是应用 AsyncContext。

Async&Await 简单利用

运用Async&Await的最重要目标是便宜开始展览异步操作,因为.net
四.0
在此以前进行异步操作时相比较复杂的,主倘诺透过调用微软提供的异步回调方法开始展览编制程序,若是超过供给协调完毕的措施显得非常胃痛,.net的次第版本都有友好首推的技巧,像.NET一.第11中学的委托,.NET2.0中的泛型,.NET三.0中的Linq,.NET肆.0中的Dynimac,.net四.五首荐的正是异步编制程序,大家只必要明白TASK+异步函数就能够实现异步编制程序。

async:告诉CLOdyssey那是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等艺术。要是八个线程读写3个共享集合,使用ConcurrentDictionary是最合适的,若是不会反复修改,那就更适合选取ImmutableDictionary。而假如是局地线程只添日元素,壹些线程只移除元素,最棒使用生产者/消费者集合

(5)集合

  1. 线程安全集合是可同时被四个线程修改的可变集合。线程安全集合混合使用了细粒度锁定和无锁手艺,以保险线程被卡住的光阴最短(经常状态下是平素不打断)。对繁多线程安全集合进行枚举操作时,内部创设了该集合的1个快照(snapshot),并对这几个快速照相进行枚举操作。线程安全集合的首要优点是多个线程能够安全地对其进展走访,而代码只会被打断相当短的时间,或根本不打断

  2. ConcurrentDictionary<TKey,
    电视机alue>是数据结构中的精品,它是线程安全的,混合使用了细粒度锁定和无锁技巧,以保险超越十分陆地方下能展开飞速访问.

  3. ConcurrentDictionary<TKey, 电视机alue> 内置了AddOrUpdate,
    TryRemove,
    TryGetValue等方式。即使四个线程读写三个共享集合,使用ConcurrentDictionary<TKey,
    电视alue>是最合适的,假使不会反复修改,那就更切合采纳ImmutableDictionary<TKey,
    TValue>。而一旦是壹对线程只添英镑素,1些线程只移除元素,最棒使用生产者/消费者集合

到此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对象),而不是3个方法。能够用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一三年,即使历时近伍年,但请相信本身,代码早在伍年前就早已写完呀。只是本凡尘接一直一向没配文字发出来。。。。。。

(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
线程能够运作那几个数据修改进程,因此一段时间内只好运维贰个进度。由此,借使能够规定是“同一时半刻间只运行1段代码”的
上下文,这就不须求共同。可是假若从线程池线程(如
Task.Run)调用这么些艺术,就要求壹块了。在那种情景下,那多个数据修改进度会在单独的线程池线程中运转,并且同时修改
data.Value,因而必须同步地走访 data.Value。

  1. 不行变类型自己正是线程安全的,修改一个不可变集合是不容许的,即使使用多少个Task.Run向聚集中添加多少,也并不供给同步操作

  2. 线程安全集合(例如
    ConcurrentDictionary)就完全不一样了。与不可变集合差别,线程安
    全集合是足以修改的。线程安全集合本身就富含了全部的一路功效

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

本例中,运转了两个并发运转的修改进度。须要1块啊?答案是“看情状”。假使能明确这些法子是在 GUI 或 ASP.NET
上下文中调用的(或同一时间内只允许1段代码运转的任
何其余上下文),那就不供给共同,因为那七个修改数据经过的运营时刻是互不相同的。
例如,假使它在 GUI 上下文中运作,就唯有叁个 UI
线程能够运作那一个数据修改进程,因而壹段时间内只好运转叁个进度。由此,假设能够明显是“同临时间只运维一段代码”的
上下文,那就不必要1块。然而壹旦从线程池线程(如
Task.Run)调用这么些主意,就必要共同了。在那种状态下,那多个数据修改进度会在单身的线程池线程中运转,并且还要修改
data.Value,由此必须同步地拜会 data.Value。

  1. 不足变类型自身就是线程安全的,修改两个不可变集合是不恐怕的,尽管使用多少个Task.Run向聚集中添增添少,也并不要求同步操作

  2. 线程安全集合(例如
    ConcurrentDictionary)就全盘两样了。与不可变集合分裂,线程安
    全集合是可以修改的。线程安全集合本身就带有了具有的一齐成效

  3. 至于锁的行使,有四条首要的守则

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

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

(柒) 实用技能

  1. 次第的三个部分共享了2个能源,今后要在第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;
}

只是,也说不定是近年来写文字的力量有着进级,所以就成功了四和5。

不然那线程安全的小说恐怕还要拖。。。。。。。。哈哈

 后记

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

 


注:此小说为原创,欢迎转发,请在小说页面分明地方给出此文链接!
若您以为那篇作品还可以请点击下右下角的推荐,万分感激!

发表评论

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

网站地图xml地图