异步编制程序,并发编制程序经典实例

 CancellationToken的多样选用

  • 1.前言
  • 贰.心直口快
  • 三.开发规范和中央
    • (一)并发编制程序概述
    • (二)异步编制程序基础
    • (3)并行开发的底子
    • (四)测试技术
    • (5)集合
    • (6)函数式OOP
    • (7)同步
  • 1.前言
  • 二.干脆俐落
  • 三.支出原则和要义
    • (一)并发编制程序概述
    • (二)异步编制程序基础
    • (三)并行开发的基础
    • (4)测试技术
    • (5)集合
    • (6)函数式OOP
    • (7)同步

前言

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

这是线程安全的终极一篇了,首要介绍CancellationToken的多样采用。

1.前言

前不久趁着体系的一段平稳期研读了千千万万书本,当中《C#出现编制程序经典实例》给自家的回想依旧相比深远的。当然,那大概是由于近段日子看的书大多嘴炮大于实际,如《Head
First设计情势》《Cracking the coding
interview》等,所以突然见到1本打着“实例”旗号的书本,照旧挺让作者觉着耳目一新。本着分享和加重领悟的指标,小编专门整理了一部分笔记(首借使Web开发中易于涉及的剧情,所以有的章节如数据流,大切诺基X等本人看了看就间接跳过了),以供审阅学习。语言和技能的吸重力,真是不得捉摸

1.前言

近年趁着类别的一段平稳期研读了众多书籍,当中《C#出现编制程序经典实例》给本人的影像照旧比较深远的。当然,那大概是由于近段日子看的书大多嘴炮大于实际,如《Head
First设计格局》《Cracking the coding
interview》等,所以突然见到一本打着“实例”旗号的书籍,照旧挺让笔者觉着耳目1新。本着分享和加重驾驭的指标,作者专门整理了有的笔记(首即使Web开发中简单涉及的剧情,所以某些章节如数据流,RX等作者看了看就一向跳过了),以供审阅学习。语言和技术的吸重力,真是不得捉摸

怎么样是异步?

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

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

贰.直截了当

一贯以来都有1种观点是达成底层架构,编写驱动和引擎,恐怕是框架和工具开发的才是高档开发职员,做上层应用的人单纯是“码农”,其实能够选取好平台提供的连锁类库,而不是全体使用底层技术和好达成,开发出高品质,稳定的应用程序,对技术能力的考验并一点都不小于开发底层库,如TPL,async,await等。

2.开宗明义

直白以来都有一种看法是兑现底层架构,编写驱动和引擎,或然是框架和工具开发的才是高档开发职员,做上层应用的人偏偏是“码农”,其实能够采纳好平台提供的相关类库,而不是1切采纳底层技术和好落成,开发出高品质,稳定的应用程序,对技术能力的考验并十分的大于开发底层库,如TPL,async,await等。

异步编制程序,并发编制程序经典实例。异步和四线程

相同点:幸免调用线程阻塞,从而抓牢软件的可响应性。

不同点:

异步操作无须额外的线程负担,并且使用回调的措施进行拍卖,在安插非凡的情事下,处理函数能够不必选用共享变量(就算不可能完全不用,最起码能够削减
共享变量的多寡),减少了死锁的大概。C#5.0 .NET4.5以往首要字Async和Await的利用,使得异步编制程序变得13分简单。

拾贰线程中的处理程序依然是各样执行,可是二十四线程的败笔也一样强烈,线程的使用(滥用)会给系统带来上下文切换的额外负担。并且线程间的共享变量或者引致死锁的产出。

二,Task运维线程,传递CancellationToken。Task传递形式分为三种,1种通过Task的参数举行传递,另一种通过向线程内传递对象的艺术传送CancellationToken。

3.付出原则和要义

三.开支条件和要义

异步应用场景及原理

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

案由:对于IO操作并不要求CPU进行过多的一个钱打二十两个结,这一个数量首要透过磁盘进行处理,假诺进展联合通讯不可能收场,必要创建更加多的线程能源,线程的数目上下文频仍的切换也是对能源的浪费,针对IO操作不需求单独的分配3个线程来拍卖。

举例表达:

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

共同处理请求的线程会被封堵,异步处理请求的线程不会堵塞。

澳门葡京备用网址 1

 

三,CancellationToken的回调函数应用。

(一)并发编制程序概述

  1. 现身:同时做多件业务
  2. 10二线程:并发的一种方式,它使用七个线程来实行顺序
  3. 并行处理:把正在实践的大气的职务分割成小块,分配给多个同时运营的线程
  4. 并行处理是十二线程的1种,而八线程是出新的壹种处理格局
  5. 异步编制程序:并发的一种情势,它利用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
    关键字对它的参数执行三个异步等待。它首先检查操作是还是不是早已做到,假如做到了,就继续运维(同步情势)。不然,它会暂停 async 方法,并赶回,留下三个未形成的
    task。一段时间后, 操作落成,async
    形式就死灰复燃运转。
  11. await代码中抛出13分后,相当会顺着Task方向前进到引用处
  12. 你如果在代码中使用了异步,最佳一贯利用。调用
    异步方法时,应该(在调用结束时)用 await 等待它回到的 task
    对象。一定要防止使用 Task.Wait 或 Task.Result
    方法,因为它们会导致死锁
  13. 线程是三个单身的运转单元,每一种进度之中有五个线程,每种线程可以独家同时施行命令。
    每一个线程有谈得来单身的栈,可是与经过内的其余线程共享内存
  14. 各种.NET应用程序都维护着二个线程池,这种意况下,应用程序差不离不须求活动创设新的线程。你若要为
    COM interop 程序创立 SAT 线程,就得 创造线程,这是绝无仅有要求线程的情况
  15. 线程是低级其余肤浅,线程池是不怎么高级一点的虚幻
  16. 并发编制程序用到的会聚有两类:并发变成+不可变集合
  17. 多数产出编制程序技术都有一个类似点:它们本质上都以函数式的。那里的函数式是作为壹种基于函数组合的编制程序格局。函数式的七个编制程序原则是精简(幸免副效用),另叁个是不变性(指一段数据不可能被改动)
  18. .NET 四.0
    引入了互动职责库(TPL),完全帮衬数据交互和天职并行。不过有个别能源较少的
    平台(例如手提式无线电话机),常常不协理 TPL。TPL 是 .NET 框架自带的

(1)并发编制程序概述

  1. 出现:同时做多件事情
  2. 多线程:并发的一种方式,它使用八个线程来实行顺序
  3. 并行处理:把正在实践的豁达的任务分割成小块,分配给多少个同时运营的线程
  4. 并行处理是102线程的壹种,而10二线程是出现的一种处理情势
  5. 异步编制程序:并发的一种情势,它利用future格局大概callback机制,防止止生出不要求的线程
  6. 异步编制程序的核情感念是异步操作:运行了的操作会在一段时间后形成。那些操作正在执行时,不会阻塞原来的线程。运行了这些操作的线程,能够继续执行其余职责。当操作达成后,会布告它的future,可能调用回调函数,以便让程序知道操作已经终结
  7. await关键字的功能:运行2个将会被实施的Task(该Task将在新线程中运作),并当即再次来到,所以await所在的函数不会被打断。当Task完结后,继续执行await后边的代码
  8. 响应式编程:并发的一种基于注脚的编制程序格局,程序在该情势中对事件作出反应
  9. 毫不用 void 作为 async 方法的归来类型! async 方法能够回到
    void,可是这仅限于编写事件处理程序。二个见惯不惊的 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 线程,就得 创制线程,那是唯1要求线程的气象
  15. 线程是低级别的画饼充饥,线程池是稍微高级一点的空洞
  16. 并发编程用到的集纳有两类:并发变成+不可变集合
  17. 多数出现编制程序技术都有1个类似点:它们本质上都以函数式的。那里的函数式是当做1种基于函数组合的编制程序格局。函数式的三个编制程序原则是不难(幸免副功用),另多少个是不变性(指1段数据不能够被涂改)
  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. 指数退避是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 方法成立并回到二个新的 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之后舍弃上下文

(2)异步编制程序基础

  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 方法成立并回到3个新的 Task 对象,那个 Task
    对象是已经 实现的,并有钦定的值
  3. 动用 IProgress 和 Progress 类型。编写的 async 方法须要有 IProgress
    参数,其 中 T 是内需告诉的速度类型,能够显得操作的快慢
  4. Task.WhenALl能够等待全体任务成功,而当每一个Task抛出相当时,能够选用性捕获极度
  5. Task.WhenAny能够等待任1职务完毕,使用它固然能够形成超时职务(当中1个Task设为Task.Delay),不过分明用专门的盈盈废除标志的过期函数处理相比好
  6. 率先章提到async和上下文的难点:在私下认可情况下,3个 async 方法在被
    await
    调用后卷土重来运营时,会在本来的上下文中运转。而足够扩大方法ConfigureAwait(false)后,则会在await之后舍弃上下文

收回义务

职分的吊销同样选取的是.NET
Framework的正儿八经裁撤操作格局,首先要求创立2个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,可用来供给并行调用一堆措施,并且那几个格局(抢先12分之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个任务,建立壹种隐 式的“父 /
    子”关系。

(三)并行开发的根基

  1. Parallel 类有1个简约的分子
    Invoke,可用来必要并行调用一堆措施,并且这几个艺术(抢先56%)是互为独立的

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

职务完毕后活动运营新职分

事实上的支付使用中,常常出现2次任务实现后立时运转此外三个职责,并且不可能使线程阻塞,在职务未有形成时调用result会使程序阻塞,不能够查看职务的履行进程,TASK提供了3个艺术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的回调函数应用:

(4)测试技术

  1. MSTest从Visual Studio二零一一 版本起头补助 async Task 类型的单元测试
  2. 比方单元测试框架不援助 async Task
    类型的单元测试,就必要做一些额外的修改才能等待异步操作。个中1种做法是使用
    Task.Wait,并在有错误时拆开 AggregateException 对象。作者的建议是利用
    NuGet 包 Nito.AsyncEx 中的 AsyncContext 类

此间附上1个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。

(4)测试技术

  1. MSTest从Visual Studio2011 版本开端援助 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中的泛型,.NET3.0中的Linq,.NET4.0中的Dynimac,.net4.伍主推的正是异步编制程序,我们只须要精通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. 线程安全集合是可同时被几个线程修改的可变集合。线程安全集合混合使用了细粒度锁定和无锁技术,以保险线程被堵塞的日子最短(日常状态下是根本不封堵)。对数不胜数线程安全集合举行枚举操作时,内部创造了该集合的2个快照(snapshot),并对这一个快照举办枚举操作。线程安全集合的最首要优点是多个线程能够高枕无忧地对其开始展览访问,而代码只会被卡住非常的短的岁月,或根本不封堵

  2. ConcurrentDictionary<TKey,
    电视alue>是数据结构中的精品,它是线程安全的,混合使用了细粒度锁定和无锁技术,以管教半数以上意况下能拓展高效访问.

  3. ConcurrentDictionary<TKey, 电视机alue> 内置了AddOrUpdate,
    TryRemove,
    TryGetValue等措施。借使多少个线程读写多个共享集合,使用ConcurrentDictionary<TKey,
    电视alue>是最合适的,如若不会1再修改,那就更符合利用ImmutableDictionary<TKey,
    TValue>。而1旦是壹些线程只添台币素,一些线程只移除成分,最佳使用生产者/消费者集合

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

(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. 在编排异步事件处理器时,事件参数类最棒是线程安全的。要成功那点,最简便的主意就是让它成为不可变的(即把富有的性质都设为只读)

(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. 在编辑异步事件处理器时,事件参数类最佳是线程安全的。要做到那一点,最简便易行的方法就是让它成为不可变的(即把具备的属性都设为只读)

就算如此第二篇小说是20一三年,即使历时近5年,但请相信自身,代码早在伍年前就曾经写完呀。只是自笔者一向一向一向没配文字发出来。。。。。。

(7)同步

  1. 同步的品类重要有二种:通讯和数据珍视

  2. 若是上边八个原则都满意,就必要用壹道来保卫安全共享的多少

  • 多段代码正在出现运转
  • 这几段代码在造访(读或写)同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. 不可变类型本身正是线程安全的,修改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. 1块的类型首要有三种:通讯和数据尊崇

  2. 万一下边四个原则都满意,就须求用协同来体贴共享的多少

  • 多段代码正在出现运行
  • 这几段代码在走访(读或写)同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
线程能够运营那些数据修改进程,因而壹段时间内只好运转3个进度。由此,借使能够规定是“同暂且间只运转1段代码”的
上下文,那就不须要壹块。不过假如从线程池线程(如
Task.Run)调用那么些法子,就要求1起了。在那种状态下,这七个数据修改进程会在单独的线程池线程中运作,并且还要修改
data.Value,因而必须共同地拜会 data.Value。

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

  2. 线程安全集合(例如
    ConcurrentDictionary)就全盘两样了。与不可变集合差异,线程安
    全集合是足以修改的。线程安全集合自身就富含了独具的多只功效

  3. 有关锁的采纳,有4条重点的轨道

  • 限定锁的效率范围(例如把lock语句使用的对象设为私有成员)
  • 文书档案中写清锁的成效内容
  • 澳门葡京备用网址,锁范围内的代码尽量少(锁定时毫无进行围堵操作)
  • 在控制锁的时候不要运营随意的代码(不要在讲话中调用事件处理,调用虚拟方法,调用委托)
  1. 若果急需异步锁,请尝试 SemaphoreSlim

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

(柒) 实用技巧

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

不过,也大概是近年写文字的能力有所升高,所以就完了了四和伍。

再不那线程安全的稿子大概还要拖。。。。。。。。哈哈

 后记

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

 


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

相关文章

发表评论

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

*
*
Website