线程安全选取,线程也疯狂

By admin in 4858.com on 2019年4月2日

 CancellationToken的五种应用

  • 1.前言
  • 二.直言不讳
  • 3.支出条件和要义
    • (一)并发编制程序概述
    • (二)异步编制程序基础
    • (三)并行开发的底子
    • (四)测试技术
    • (5)集合
    • (6)函数式OOP
    • (7)同步
  • 1.前言
  • 二.直截了当
  • 3.支付规范和要义
    • (1)并发编制程序概述
    • (2)异步编制程序基础
    • (3)并行开发的根基
    • (4)测试技术
    • (5)集合
    • (6)函数式OOP
    • (7)同步

前言

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

那是线程安全的末段壹篇了,首要介绍CancellationToken的有余利用。

1.前言

多年来趁着项目标1段平稳期研读了好多图书,在那之中《C#出现编制程序经典实例》给本身的回忆照旧相比深切的。当然,那恐怕是由于近段日子看的书大多嘴炮大于实际,如《Head
First设计情势》《Cracking the coding
interview》等,所以突然见到一本打着“实例”旗号的图书,照旧挺让笔者认为耳目一新。本着分享和加剧精晓的目标,作者尤其整理了1部分笔记(首假设Web开发中易于涉及的始末,所以部分章节如数据流,昂CoraX等本人看了看就径直跳过了),以供审阅学习。语言和技能的魔力,真是不得捉摸

1.前言

近些年趁着项指标一段平稳期研读了不少图书,在那之中《C#出现编制程序经典实例》给自身的影像如故比较深远的。当然,那或然是由于近段日子看的书大多嘴炮大于实际,如《Head
First设计格局》《Cracking the coding
interview》等,所以突然见到壹本打着“实例”旗号的书本,依旧挺让小编觉着耳目1新。本着分享和加剧理解的目的,小编特意整理了有的笔记(首假若Web开发中不难涉及的始末,所以有的章节如数据流,QX56X等笔者看了看就直接跳过了),以供审阅学习。语言和技术的魔力,真是不得捉摸

怎么着是异步?

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

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

二.斩钉截铁

直白以来都有一种看法是完结底层架构,编写驱动和引擎,恐怕是框架和工具开发的才是高档开发人士,做上层应用的人单纯是“码农”,其实能够采用好平台提供的连锁类库,而不是全体选拔底层技术和好完毕,开发出高品质,稳定的应用程序,对技术能力的考验并十分大于开发底层库,如TPL,async,await等。

二.开门见山

直接以来都有壹种意见是兑现底层架构,编写驱动和引擎,或许是框架和工具开发的才是高档开发职员,做上层应用的人偏偏是“码农”,其实能够采纳好平台提供的相关类库,而不是一切运用底层技术和好完结,开发出高质量,稳定的应用程序,对技术能力的考验并非常大于开发底层库,如TPL,async,await等。

线程安全选取,线程也疯狂。异步和多线程

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

不同点:

异步操作无须额外的线程负担,并且动用回调的章程开始展览拍卖,在陈设优秀的情事下,处理函数可以无需选择共享变量(即便不恐怕完全不用,最起码能够削减
共享变量的多寡),减少了死锁的只怕。C#伍.0 .NET4.5以后首要字Async和Await的利用,使得异步编程变得10分容易。

10二线程中的处理程序照旧是逐壹执行,不过八线程的毛病也一样为之侧目,线程的使用(滥用)会给系统带来上下文切换的额外负担。并且线程间的共享变量恐怕造成死锁的面世。

二,Task运维线程,传递CancellationToken。Task传递方式分为二种,一种通过Task的参数举行传递,另一种通过向线程内传递对象的措施传送CancellationToken。

三.支付规范和要义

三.支出原则和要义

异步应用场景及原理

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

案由:对于IO操作并不需求CPU举办过多的持筹握算,这几个数量重要透过磁盘举办处理,假诺展开联合通讯无法收场,供给创设更加多的线程财富,线程的数目上下文频仍的切换也是对财富的浪费,针对IO操作不要求单独的分配1个线程来处理。

举例表明:

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

联手处理请求的线程会被打断,异步处理请求的线程不会卡住。

4858.com 1

 

3,CancellationToken的回调函数应用。

(1)并发编制程序概述

  1. 出现:同时做多件工作
  2. 二十三四线程:并发的一种情势,它使用多个线程来推行顺序
  3. 并行处理:把正在执行的大气的任务分割成小块,分配给八个同时运行的线程
  4. 并行处理是八线程的一种,而10贰线程是出新的1种处理格局
  5. 异步编制程序:并发的1种格局,它利用future情势只怕callback机制,避防止爆发不需要的线程
  6. 异步编制程序的核心理念是异步操作:运转了的操作会在一段时间后完结。那几个操作正在推行时,不会堵塞原来的线程。运营了这么些操作的线程,能够继续执行其余任务。当操作实现后,会通报它的future,可能调用回调函数,以便让程序知道操作已经告竣
  7. await关键字的职能:运转叁个将会被实践的Task(该Task将在新线程中运作),并立时赶回,所以await所在的函数不会被卡住。当Task达成后,继续执行await后边的代码
  8. 响应式编制程序:并发的一种基于声明的编制程序格局,程序在该情势中对事件作出反应
  9. 毫无用 void 作为 async 方法的回到类型! async 方法能够回来
    void,然则那仅限于编写事件处理程序。3个不足为奇的 async
    方法假若未有重回值,要回到 Task,而不是 void
  10. async 方法在初步时以协同格局执行。在 async 方法内部,await
    关键字对它的参数执行叁个异步等待。它首先检查操作是或不是已经形成,假如形成了,就持续运行(同步格局)。不然,它会中断 async 方法,并重返,留下一个未到位的
    task。1段时间后, 操作落成,async
    艺术就过来运维。
  11. await代码中抛出非凡后,分外会沿着Task方向前进到引用处
  12. 你一旦在代码中利用了异步,最佳一向利用。调用
    异步方法时,应该(在调用甘休时)用 await 等待它回到的 task
    对象。一定要幸免使用 Task.Wait 或 Task.Result
    方法,因为它们会促成死锁
  13. 线程是多个独自的周转单元,各类进程之中有四个线程,每种线程能够独家同时施行命令。
    每一种线程有友好单独的栈,不过与经过内的其余线程共享内部存款和储蓄器
  14. 各种.NET应用程序都维护着1个线程池,那种状态下,应用程序大概不要求活动创制新的线程。你若要为
    COM interop 程序创制 SAT 线程,就得 创造线程,那是绝无仅有须要线程的场馆
  15. 线程是低级别的抽象,线程池是不怎么高级一点的用空想来安慰自己
  16. 并发编制程序用到的集结有两类:并发变成+不可变集合
  17. 超过一半油可是生编制程序技术都有3个类似点:它们本质上都以函数式的。那里的函数式是用作壹种基于函数组合的编制程序格局。函数式的三个编制程序原则是精简(制止副功能),另一个是不变性(指壹段数据无法被涂改)
  18. .NET 4.0
    引入了互相任务库(TPL),完全支持数据交互和天职并行。不过一些财富较少的
    平台(例如手提式有线电话机),平时不帮助 TPL。TPL 是 .NET 框架自带的

(一)并发编制程序概述

  1. 并发:同时做多件事情
  2. 十贰线程:并发的壹种样式,它利用八个线程来执行顺序
  3. 并行处理:把正在举办的大度的职分分割成小块,分配给五个同时运行的线程
  4. 并行处理是多线程的壹种,而二十多线程是出现的一种处理方式
  5. 异步编制程序:并发的1种样式,它选择future形式只怕callback机制,以幸免发生不需求的线程
  6. 异步编制程序的主题思量是异步操作:运维了的操作会在一段时间后完毕。那几个操作正在实施时,不会堵塞原来的线程。运转了那一个操作的线程,能够继续执行别的职分。当操作完毕后,会文告它的future,或然调用回调函数,以便让程序知道操作已经终止
  7. await关键字的效劳:运维三个将会被实施的Task(该Task将在新线程中运维),并马上赶回,所以await所在的函数不会被卡住。当Task实现后,继续执行await前面的代码
  8. 响应式编制程序:并发的1种基于申明的编制程序格局,程序在该格局中对事件作出反应
  9. 毫不用 void 作为 async 方法的回来类型! async 方法能够回来
    void,然而那仅限于编写事件处理程序。三个家常便饭的 async
    方法假如未有再次来到值,要重回 Task,而不是 void
  10. async 方法在开首时以协同格局履行。在 async 方法内部,await
    关键字对它的参数执行3个异步等待。它首先检查操作是还是不是已经完毕,要是成功了,就再三再四运转(同步方式)。不然,它会停顿 async 方法,并回到,留下一个未成功的
    task。1段时间后, 操作实现,async
    方法就恢复生机械运输维。
  11. await代码中抛出1贰分后,非常会沿着Task方向前进到引用处
  12. 您若是在代码中央银行使了异步,最棒平素采纳。调用
    异步方法时,应该(在调用甘休时)用 await 等待它回到的 task
    对象。一定要防止采取 Task.Wait 或 Task.Result
    方法,因为它们会招致死锁
  13. 线程是二个独自的运作单元,各类进度之中有多少个线程,每一个线程能够分级同时实施命令。
    每一种线程有自己独自的栈,不过与经过内的别的线程共享内部存款和储蓄器
  14. 每一个.NET应用程序都维护着三个线程池,那种地方下,应用程序大约不需求活动成立新的线程。你若要为
    COM interop 程序成立 SAT 线程,就得 创造线程,那是唯壹需求线程的景色
  15. 线程是低级其余悬空,线程池是稍微高级一点的架空
  16. 并发编制程序用到的集纳有两类:并发变成+不可变集合
  17. 大多数涌出编制程序技术都有一个类似点:它们本质上都以函数式的。这里的函数式是用作一种基于函数组合的编制程序情势。函数式的三个编制程序原则是不难(幸免副功用),另1个是不变性(指1段数据不能被涂改)
  18. .NET 四.0
    引入了相互职责库(TPL),完全帮衬数据交互和职责并行。不过有个别财富较少的
    平台(例如手机),经常不帮助 TPL。TPL 是 .NET 框架自带的

任务

  在运用职务在此之前,针对线程的调用大多都用线程池提供的静态方法QueueUserWorkItem,不过那一个函数有成都百货上千的限量,当中最大的题材就算从未内部机制能够让开发者知道操作在怎么着时候做到,也未曾机制在操作达成时获得重返值,微软为了解决这么些题材引入了义务的概念。

 首先构造1个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职责完结,使用它即使可以成功超时职分(其中一个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抛出11分时,可以选择性捕获极度
  5. Task.WhenAny能够等待任1职责成功,使用它就算可以成功超时职务(在那之中一个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 另一个职务,建立1种隐 式的“父 /
    子”关系。

(叁)并行开发的根底

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

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

实则的开支应用中,日常出现一回职务实现后当即运营此外三个任务,并且不可见使线程阻塞,在职务未有完成时调用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的回调函数应用:

4858.com ,(四)测试技术

  1. MSTest从Visual Studio二〇一三 版本早先帮助 async Task 类型的单元测试
  2. 要是单元测试框架不帮忙 async Task
    类型的单元测试,就供给做1些外加的修改才能等待异步操作。在那之中一种做法是利用
    Task.Wait,并在有错误时拆开 AggregateException 对象。笔者的提出是运用
    NuGet 包 Nito.AsyncEx 中的 AsyncContext 类

那边附上3个ABP中落到实处的可操作AsyncHelper类,正是依据AsyncContext达成

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

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

        /// <summary>
        /// Runs a async method synchronously.
        /// </summary>
        /// <param name="action">An async action</param>
        public static void RunSync(Func<Task> action)
        {
            AsyncContext.Run(action);
        }
    }
  1. 在 async 代码中,关键准则之壹就是幸免采取 async
    void。小编相当建议大家在对 async void
    方法做单元测试时展开代码重构,而不是应用 AsyncContext。

(四)测试技术

  1. MSTest从Visual Studio2013 版本开始协助 async Task 类型的单元测试
  2. 假诺单元测试框架不扶助 async Task
    类型的单元测试,就需求做1些附加的改动才能等待异步操作。个中1种做法是接纳Task.Wait,并在有错误时拆开 AggregateException 对象。小编的提出是使用
    NuGet 包 Nito.AsyncEx 中的 AsyncContext 类

那里附上一个ABP中落到实处的可操作AsyncHelper类,正是依据AsyncContext完结

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

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

        /// <summary>
        /// Runs a async method synchronously.
        /// </summary>
        /// <param name="action">An async action</param>
        public static void RunSync(Func<Task> action)
        {
            AsyncContext.Run(action);
        }
    }
  1. 在 async 代码中,关键准则之一便是防止选择 async
    void。作者丰裕建议大家在对 async void
    方法做单元测试时举行代码重构,而不是运用 AsyncContext。

Async&Await 不难利用

动用Async&Await的机要目标是惠及开始展览异步操作,因为.net
4.0
从前实行异步操作时比较复杂的,主假设通过调用微软提供的异步回调方法开始展览编制程序,若是遇上要求协调达成的秘籍显得万分脑仁疼,.net的相继版本都有和好主推的技术,像.NET1.第11中学的委托,.NET二.0中的泛型,.NET3.0中的Linq,.NET4.0中的Dynimac,.net四.5主推的便是异步编制程序,大家只需求精晓TASK+异步函数就足以兑现异步编制程序。

async:告诉CL奇骏那是二个异步函数。

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

 

4858.com 2

(5)集合

  1. 线程安全集合是可同时被多少个线程修改的可变集合。线程安全集合混合使用了细粒度锁定和无锁技术,以担保线程被堵塞的日子最短(常常状态下是常有不打断)。对广大线程安全集合举办枚举操作时,内部成立了该集合的3个快速照相(snapshot),并对那些快速照相实行枚举操作。线程安全集合的首要性优点是七个线程可以安全地对其开展访问,而代码只会被卡住不够长的岁月,或根本不打断

  2. ConcurrentDictionary是数据结构中的精品,它是线程安全的,混合使用了细粒度锁定和无锁技术,以确定保证大多数境况下能展开火速访问.

  3. ConcurrentDictionary 内置了AddOrUpdate, TryRemove,
    TryGetValue等方式。要是八个线程读写三个共享集合,使用ConcurrentDictionary是最合适的,假使不会1再修改,那就更适合利用ImmutableDictionary。而只若是壹些线程只添日元素,1些线程只移除成分,最佳使用生产者/消费者集合

(5)集合

  1. 线程安全集合是可同时被多个线程修改的可变集合。线程安全集合混合使用了细粒度锁定和无锁技术,以确定保障线程被封堵的日子最短(常常状态下是有史以来不封堵)。对许二十八线程安全集合实行枚举操作时,内部创设了该集合的二个快速照相(snapshot),并对这几个快速照相进行枚举操作。线程安全集合的主要性优点是多个线程能够高枕无忧地对其进行访问,而代码只会被堵塞非常的短的小时,或根本不封堵

  2. ConcurrentDictionary<TKey,
    TValue>是数据结构中的精品,它是线程安全的,混合使用了细粒度锁定和无锁技术,以保证超越三分之二境况下能开始展览高效访问.

  3. ConcurrentDictionary<TKey, TValue> 内置了AddOrUpdate,
    TryRemove,
    TryGetValue等方法。假诺八个线程读写2个共享集合,使用ConcurrentDictionary<TKey,
    TValue>是最合适的,假使不会频仍修改,那就更切合选用ImmutableDictionary<TKey,
    TValue>。而只尽管①些线程只添加元素,一些线程只移除元素,最棒使用生产者/消费者集合

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

(6)函数式OOP

  1. 异步编制程序是函数式的(functional),.NET
    引入的async让开发者进行异步编程的时候也能用进程式编制程序的思维来实行驰念,不过在内部贯彻上,异步编制程序如故是函数式的

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

  2. 可以用await等待的是二个类(如Task对象),而不是三个措施。能够用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. 在编排异步事件处理器时,事件参数类最棒是线程安全的。要大功告成这一点,最简便的措施正是让它成为不可变的(即把拥有的属性都设为只读)

(6)函数式OOP

  1. 异步编制程序是函数式的(functional),.NET
    引入的async让开发者实行异步编制程序的时候也能用进程式编制程序的盘算来拓展思想,不过在在那之中贯彻上,异步编制程序依旧是函数式的

    皇皇说过,世界既是进程式的,也是函数式的,可是终归是函数式的

  2. 能够用await等待的是2个类(如Task对象),而不是一个主意。能够用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. 在编辑异步事件处理器时,事件参数类最棒是线程安全的。要成功那点,最简便易行的点子就是让它成为不可变的(即把全体的性子都设为只读)

就算如此第二篇作品是20壹三年,即便历时近5年,但请相信本人,代码早在伍年前就已经写完呀。只是自身直接平昔一贯没配文字发出来。。。。。。

(7)同步

  1. 联合的门类主要有二种:通讯和数据体贴

  2. 假定上面八个规范都满意,就供给用一道来保卫安全共享的数码

  • 多段代码正在出现运转
  • 这几段代码在造访(读或写)同1个数据
  • 最少有一段代码在改动(写)数据
  1. 观看以下代码,鲜明其伙同和平运动行状态

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

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

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

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

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

    return data.Value;
}

本例中,运行了三个并发运维的改动进度。须要共同啊?答案是“看情形”。要是能鲜明这几个办法是在 GUI 或 ASP.NET
上下文中调用的(或同近来间内只同意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
    中,处理请求的代码本来就是在线程池线程中运作的,强行把它放到另三个线程池线程平时会白璧微瑕

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

(7)同步

  1. 同台的种类主要有两种:通讯和数据珍惜

  2. 倘诺上面八个标准化都满意,就须要用1道来保卫安全共享的数据

  • 多段代码正在出现运维
  • 这几段代码在造访(读或写)同贰个多少
  • 至少有1段代码在改动(写)数据
  1. 着眼以下代码,明确其3头和平运动转处境

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
线程能够运作那个多少修改进度,因此壹段时间内只可以运转2个历程。因而,如若可以显著是“同近期间只运维1段代码”的
上下文,那就不须要一起。可是只要从线程池线程(如
Task.Run)调用这么些办法,就必要联合了。在那种情况下,那八个数据修改进程会在独立的线程池线程中运维,并且还要修改
data.Value,因而必须壹起地访问 data.Value。

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

  2. 线程安全集合(例如
    ConcurrentDictionary)就完全分裂了。与不可变集合不一致,线程安
    全集合是足以修改的。线程安全集合本人就包括了具备的联合署名功用

  3. 有关锁的利用,有肆条至关心器重要的清规戒律

  • 限定锁的意义范围(例如把lock语句使用的对象设为私有成员)
  • 文书档案中写清锁的功能内容
  • 锁范围内的代码尽量少(锁定时不要开展围堵操作)
  • 在控制锁的时候绝不运转随意的代码(不要在讲话中调用事件处理,调用虚拟方法,调用委托)
  1. 假定必要异步锁,请尝试 塞马phoreSlim

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

可是,也也许是近年写文字的力量有着升级,所以就成功了四和5。

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

 后记

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

 


注:此小说为原创,欢迎转发,请在小说页面分明地点给出此文链接!
若您觉得那篇小说还不易请点击下右下角的推荐,卓殊感激!

发表评论

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

网站地图xml地图
Copyright @ 2010-2019 美高梅手机版4858 版权所有