您的位置:新葡亰496net > 奥门新萄京娱乐场 > 异步编制程序,使用职务并行库

异步编制程序,使用职务并行库

发布时间:2019-06-16 12:41编辑:奥门新萄京娱乐场浏览(188)

    目录

    复习:

     IO操作的MDA(Direct memory access)情势:间接待上访问内部存款和储蓄器,是一种不经过CPU而直白开始展览内部存款和储蓄器数据存款和储蓄的数据沟通形式,大概能够不损耗CPU的财富;
     CLKuga所提供的异步编程模型正是充裕利用硬件的DMA功效来刑释CPU的压力;使用线程池举行保管,异步将职业移交给线程池中的有个别专门的职业线程来完结,直到异步完毕,异步才会透过回调的秘籍通告线程池,让CLEnclave响应异步实现;

    本文内容

    • 概述
    • 编纂异步方法
    • 异步程序中的调整流
    • API 异步方法
    • 线程
    • 异步和等待
    • 回去类型和参数
    • 参谋资料

    本文内容

    • 概述
    • 编写异步方法
    • 异步程序中的调节流
    • API 异步方法
    • 线程
    • 异步和等候
    • 回去类型和参数
    • 参照他事他说加以调查资料
    • 1.1 简介
    • 1.2 成立职责
    • 1.3 使用职务推行基本的操作
    • 1.4 组合职分
    • 1.5 将APM情势转变为天职
    • 1.6 将EAP形式调换为天职
    • 1.7 完毕撤废选项
    • 1.8 管理职务中的非凡
    • 1.9 相互运营职务
    • 1.10 使用TaskScheduler配置职责推行
    • 参谋书籍
    • 小编水平有限,假如不当招待各位争持指正!

    其三章内容中大家提到了二种异步编制程序模型,这里大致复习一下,分别如下

    它是出现的一种格局,它应用 future 情势或回调(callback)机制,以免止发出不须求的线程。一个 future(或 promise)类型代表有个别就要实现的操作。在 .NET 中,新版 future 类型有Task 和Task<TResult>。 

    下载 Demo

    下载 Demo


    1.APM(异步编制程序形式):形如Beginxxx,Endxxx。

    异步编制程序方式------使用委托和线程池完成的方式

    APM 异步编制程序模型,Asynchronous Programming Model            C#1.0

    EAP 基于事件的异步编制程序格局,Event-based Asynchronous Pattern  C#2.0

    TAP 基于任务的异步编制程序情势,Task-based Asynchronous Pattern    C#4.0

    Asyncawait简化异步编制程序;职务并行库,Task Parallel Library     C#5

    下载 Demo TPL 与 APM 和 EAP 结合(APM 和 EAP 那多个正规异步情势已经不能够适应多核时期,但前边用那三种方法写的代码怎么做?——把它们退换一下,跟 TPL 结合)

    下载 德姆o TPL 与 APM 和 EAP 结合(APM 和 EAP 那多少个正经异步方式已经无法适应多核时代,但后边用那三种方法写的代码如何做?——把它们更动一下,跟 TPL 结合)

    本系列首页链接:[C#四线程编制程序类别(一)- 简要介绍 ]

    2.EAP(基于事件的异步编制程序情势):这么些大家在.net中利用到了BackgroudWorker组件,使用方式是透过事件绑定管理的措施。

    APM

             使用IAsyncResult设计形式的异步操作是通过名称为 BeginXXX 和 EndXXX 的多少个措施来落实,那四个艺术分别指发轫和终结异步操作。该情势允许用更少的CPU能源(线程)去做越多的操作,.NET Framework繁多类也达成了该方式,相同的时间我们也得以自定义类来贯彻该情势(约等于在自定义的类中完成再次来到类型为IAsyncResult接口的BeginXXX方法和经受IAsyncResult包容类型作为唯一参数的EndXXX方法),别的事委员会托项目也定义了BeginInvoke和EndInvoke方法。举例,FileStream类提供BeginRead和EndRead方法来从文件异步读取字节。那三个主意完毕了 Read 方法的异步版本。

    调用 BeginXXX 后,应用程序能够持续在调用线程上试行命令,同期异步操作在另壹个线程上施行(要是有重回值还应调用 EndXXX告竣异步操作,并向该措施传递BeginXXX 方法重临的IAsyncResult对象,获得操作的返回值)。

     图片 1

    CompletedSynchronously属性值侧重与提醒音信,而非操作

    走访异步操作的结果,APM提供了八种情势:

    1.在调用BeginXXX方法的线程上调用EndXXX方法来获得异步操作的结果;不过这种措施会阻塞调用线程,在领悟操作完毕之后调用线程技能承继运营。

    2.循环查询IAsyncResult的IsComplete属性,操作完成后再调用EndXXX方法来获得操作再次来到的结果。

    3.IAsyncResult的AsyncWaitHandle属性落成更灵活的等候逻辑,调用该属性WaitOne()方法来使一个线程阻塞并伺机操作完结;再调用EndXXX方法来获得操作的结果。WaitHandle.WaitOne()能够钦赐最长的守候时间,如超时再次回到false;

    4. 在调用BeginXXX方法时提供AsyncCallback委托的实例作为参数,在异步操作落成后委托会自动调用(AsyncCallback对象)钦点的章程。(首荐办法)AsyncCallback委托仅可以调用符合一定方式的方法(唯有二个参数IAsyncResult,且尚未再次来到值);

    图片 2图片 3

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Threading;
    using System.Runtime.Remoting.Messaging;
    
    namespace AsyncCallbackDelegate
    {
        public delegate int BinaryOp(int x, int y);
        class Program
        {
            private static bool isDone = false;
            static void Main(string[] args)
            {
                Console.WriteLine("*****  AsyncCallbackDelegate Example *****");
                Console.WriteLine("Main() invoked on thread {0}.",
                  Thread.CurrentThread.ManagedThreadId);
                BinaryOp b = new BinaryOp(Add);
                IAsyncResult iftAR = b.BeginInvoke(10, 10,
                  new AsyncCallback(AddComplete),
                  "Main() thanks you for adding these numbers.");//传入数据
                // Assume other work is performed here...
                while (!isDone)
                {
                    Thread.Sleep(1000);
                    Console.WriteLine("Working....");
                }
                Console.ReadLine();
            }
    
            #region Target for AsyncCallback delegate
            // Don't forget to add a 'using' directive for 
            // System.Runtime.Remoting.Messaging!
            static void AddComplete(IAsyncResult itfAR)
            {
                Console.WriteLine("AddComplete() invoked on thread {0}.",
                  Thread.CurrentThread.ManagedThreadId);
                Console.WriteLine("Your addition is complete");
    
                // Now get the result.
                //AsyncCallback委托的目标无法调用其他方法中创建的委托
                //IAsyncResult itfAR 实际上是System.Runtime.Remoting.Messaging命名空间AsyncResult类的一个实例
                AsyncResult ar = (AsyncResult)itfAR;
                //AsyncDelegate静态属性返回原始异步委托引用
                BinaryOp b = (BinaryOp)ar.AsyncDelegate;
                Console.WriteLine("10   10 is {0}.", b.EndInvoke(itfAR));
    
                // Retrieve the informational object and cast it to string.
                //AsyncState属性获取 BeginInvoke第四个参数传入的值
                string msg = (string)itfAR.AsyncState;
                Console.WriteLine(msg);
                isDone = true;
            }
    
            #endregion
    
            #region Target for BinaryOp delegate
            static int Add(int x, int y)
            {
                Console.WriteLine("Add() invoked on thread {0}.",
                  Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(5000);
                return x   y;
            }
            #endregion
        }
    }
    

    AsyncCallback

    特别捕获

    在一块儿实践的艺术里面普通管理极度的不二等秘书诀是将恐怕抛出特其他代码放到try...catch...finally里面,之所以能够捕获到,是因为爆发格外的代码与调用的代码位于同四个线程。当调用二个异步方法爆发非常时,CL奥迪Q7会捕获并且在EndXXX方法时再也将丰硕抛出抛出,所以异步调用中的极度在EndXXX方法出捕获就行了。

    class ApmExceptionHandling 
    {
       public static void Go() 
      {
          WebRequest webRequest = WebRequest.Create("http://0.0.0.0/");
          webRequest.BeginGetResponse(ProcessWebResponse, webRequest);
          Console.ReadLine();
       }
       private static void ProcessWebResponse(IAsyncResult result) {
          WebRequest webRequest = (WebRequest)result.AsyncState;
    
          WebResponse webResponse = null;
          try {
             webResponse = webRequest.EndGetResponse(result);
             Console.WriteLine("Content length: "   webResponse.ContentLength);
          }
          catch (WebException we) {
             Console.WriteLine(we.GetType()   ": "   we.Message);
          }
          finally {
             if (webResponse != null) webResponse.Close();
          }
       }
    }
    

    APM WinForm UI线程回调

    鉴于AsyncCallback委托回调是从ThreadPool中的线程执行的,由此对于Winform,假若回调须求操作UI控件,就需求回到到UI线程去,常用的七个点子:

    1.  Control类达成了ISynchronizeInvoke接口,提供了Invoke和BeginInvoke方法来帮助其余线程更新GUI分界面控件的建制(将回调方法投递到开创该控件的线程中进行)。

     图片 4

    Control类的 Invoke,BeginInvoke 内部贯彻如下:

    a) Invoke(同步调用)先剖断控件创造线程与日前线程是或不是一律,一样则直接调用委托方法;否则使用Win32API的PostMessage异步推行,不过Invoke内部会调用IAsyncResult.AsyncWaitHandle等待实践到位。

    b) BeginInvoke(异步调用)使用Win32API的PostMessage 异步实行,并且再次来到IAsyncResult 对象。

    选择格局:回调方法中对控件检查测试InvokeRequired值,if true,在该回调中封送一次委托,调用控件的Invoke/ BeginInvoke方法;

     图片 5

    2.GUI(WinForm/WPF)应用程序引进了二个线程管理模型:创设窗口的线程是唯一能对丰硕窗口进行创新的线程;在GUI线程中,平日索要转变异步操作,使GUI线程不打断并甘休响应用户输入。但是,异步操作完结时,由于是用贰个线程池线程实现的,而线程池线程不可能更新UI控件。为缓和这几个主题材料,FCL定义贰个System.Threading.SynchronizationContext(线程同步上下文)的基类,其派生对象承担将二个应用程序模型连接到它的线程处理模型。

    GUI线程都有多少个和它事关的SynchronizationContext派生对象,使用其静态Current属性获取:SynchronizationContext sc = SynchronizationContext.Current; 将此指标传给其余线程,当叁个线程池线程要求让GUI线程更新UI时,调用该指标的sc.Post方法,向Post传递一个相称SendOrPostCallback委托签字的回调方法(一般是更新UI的操作方法,由GUI线程去施行),以及二个要传给回调方法的实参。

    SynchronizationContext 的Post方法和Send方法的界别:(分别对应于异步/同步调用)

    Post方法将回调方法送人GUI线程的队列,允许程序池线程立即返回,不进行阻塞;Post方法内部调用了BeginInvoke方法;
    
    Send方法也将回调方法送人GUI线程的队列,但随后就会阻塞线程池线程,直到GUI线程完成对回调方法的调用。阻塞线程池线程极有可能造成线程池创建一个新的线程,避免调用该方法;Send方法内部调用了Invoke方法; 
    

    对winform来讲是 System.Windows.Forms.WindowsFormsSynchronizationContext是其子类.

    Winform窗口冒出后,UI线程 SynchronizationContext.Current会被绑定赋值,唯有UI线程的Current不为null。

    Public class SendOrPostUI {
       public static void Go() {
          System.Windows.Forms.Application.Run(new MyWindowsForm());
       }
       private static AsyncCallback SyncContextCallback(AsyncCallback callback) {
          // Capture the calling thread's SynchronizationContext-derived object
          SynchronizationContext sc = SynchronizationContext.Current;
          // If there is no SC, just return what was passed in
          if (sc == null) return callback;
          // Return a delegate that, when invoked, posts to the captured SC a method that 
          // calls the original AsyncCallback passing it the IAsyncResult argument
          return asyncResult => sc.Post(result => callback((IAsyncResult)result), asyncResult);
       }
       private sealed class MyWindowsForm : System.Windows.Forms.Form {
          public MyWindowsForm() {
             Text = "Click in the window to start a Web request";
             Width = 400; Height = 100;
          }
          protected override void OnMouseClick(System.Windows.Forms.MouseEventArgs e) {
             // The GUI thread initiates the asynchronous Web request 
             Text = "Web request initiated";
             var webRequest = WebRequest.Create("http://Wintellect.com/");
             webRequest.BeginGetResponse(SyncContextCallback(ProcessWebResponse), webRequest);
             base.OnMouseClick(e);
          }
          private void ProcessWebResponse(IAsyncResult result) {
             // If we get here, this must be the GUI thread, it's OK to update the UI
             var webRequest = (WebRequest)result.AsyncState;
             using (var webResponse = webRequest.EndGetResponse(result)) {
                Text = "Content length: "   webResponse.ContentLength;
             }
          }
       }
    }
    

    正如三种艺术其实差不太多,叁个是回调内重新卷入,一个是包装原本的回调。可是SynchronizationContext业务层与UI分离来讲的话是相比较好;

    概述


    异步对也许起阻止功用的位移(举例,应用程序访问 Web 时)至关心注重要。 对 Web 能源的走访不经常比异常的慢或会推迟。 倘诺此类活动在共同进程中受阻,则整个应用程序必须等待。在异步进程中,应用程序可继续实施不依赖Web 财富的其它干活,直至潜在阻止义务到位。

    下表是运用异步编制程序能增长响应本领的一枝独秀气象。从 .NET Framework 4.5 和 Windows 运营时中列出的 API 包括协助异步编制程序的点子。

    应用程序区域

    包含异步方法的受支持的 API

    Web 访问

    HttpClient ,SyndicationClient

    使用文件

    StorageFile、StreamWriter、StreamReader、XmlReader

    使用图像

    MediaCapture、BitmapEncoder、BitmapDecoder

    WCF 编程

    同步和异步操作

    是因为有着与用户分界面相关的移动一般共享三个线程,因此,异步对走访 UI 线程的应用程序来讲更是重大。借使其它进度在同步应用程序中受阻,则持有进度都将受阻。 你的应用程序截止响应,因而,你只怕在其等待进度中感觉它曾经退步。

    使用异步方法时,应用程序将继续响应 UI。 举个例子,你能够调动窗口的深浅或最小化窗口;若是您不指望等待应用程序截至,则足以将其倒闭。

    能够动用三种格局来兑现 TAP:即手动使用 C# 编写翻译器,或将编写翻译器和手动方法结合使用。使用 TAP 形式来完毕计算密集型和 I/O 密集型异步操作。

    • 动用编写翻译器。在 Visual Studio 二〇一一 和 .NET Framework 4.5中,任何具备 async 关键字的办法都被作为是一种异步方法,并且 C# 会实践供给的转变,以通过 TAP 来异步落成该情势。 异步方法应再次来到System.Threading.Tasks.Task 或 System.Threading.Tasks.Task<TResult> 对象。
    • 手动生成 TAP 方法。也足以手动达成 TAP 方式,以更加好地垄断(monopoly)达成。编写翻译器重视从 System.Threading.Tasks 命名空间公开的集体外围应用和 System.Runtime.CompilerServices 命名空间中帮衬的档期的顺序。 如要自个儿达成 TAP,你需求创建一个TaskCompletionSource<TResult> 对象、实施异步操作,并在操作达成时,调用 SetResult、SetException、SetCanceled 方法,或调用那么些措施之一的Try版本。 手动完成 TAP 方法时,需在所表示的异步操作完结时形成生成的天职。 比如:
    • 掺杂方法。您大概发掘手动完毕 TAP 形式、但将落到实处大旨逻辑委托给编写翻译器的这种方法很有用。 比方,当你想要验证编写翻译器生成的异步方法之外的实参时,可能必要利用这种混合方法,以便万分能够转义到该措施的第一手调用方而不是由此System.Threading.Tasks.Task 对象被公开:

    本文主要说明“使用编写翻译器”方法。

    概述


    异步对恐怕起阻止功效的位移(比如,应用程序访问 Web 时)至关心保养要。 对 Web 财富的走访不常不快或会推迟。 假设此类活动在共同进程中受阻,则整个应用程序必须等待。在异步进度中,应用程序可继续实践不依赖Web 能源的别的干活,直至潜在阻止职务到位。

    下表是选取异步编制程序能拉长响应技能的特出气象。从 .NET Framework 4.5 和 Windows 运转时中列出的 API 包罗援助异步编制程序的诀要。

    应用程序区域

    包含异步方法的受支持的 API

    Web 访问

    HttpClient ,SyndicationClient

    使用文件

    StorageFile、StreamWriter、StreamReader、XmlReader

    使用图像

    MediaCapture、BitmapEncoder、BitmapDecoder

    WCF 编程

    同步和异步操作

    鉴于有着与用户分界面相关的移动一般共享一个线程,由此,异步对走访 UI 线程的应用程序来讲更是重大。尽管此外进度在同步应用程序中受阻,则有所进度都将受阻。 你的应用程序截止响应,因此,你或者在其等待进程中感到它曾经战败。

    利用异步方法时,应用程序将继续响应 UI。 比如,你可以调动窗口的高低或最小化窗口;即便您不指望等待应用程序甘休,则足以将其停业。

    能够使用三种办法来兑现 TAP:即手动使用 C# 编写翻译器,或将编写翻译器和手动方法结合使用。使用 TAP 方式来实现总括密集型和 I/O 密集型异步操作。

    • 使用编写翻译器。在 Visual Studio 二〇一二 和 .NET Framework 4.5中,任何具备 async 关键字的主意都被看作是一种异步方法,并且 C# 会施行须要的转变,以通过 TAP 来异步完结该办法。 异步方法应重临System.Threading.Tasks.Task 或 System.Threading.Tasks.Task<TResult> 对象。
    • 手动生成 TAP 方法。也足以手动完成 TAP 格局,以越来越好地决定完毕。编写翻译器重视从 System.Threading.Tasks 命名空间公开的集体外围应用和 System.Runtime.CompilerServices 命名空间中帮助的体系。 如要自身实现 TAP,你须要创设三个TaskCompletionSource<TResult> 对象、推行异步操作,并在操作达成时,调用 SetResult、SetException、SetCanceled 方法,或调用这么些措施之一的Try版本。 手动完毕 TAP 方法时,需在所表示的异步操作实现时成功生成的职务。 比方:
    public static Task<int> ReadTask(this Stream stream, byte[] buffer, int offset, int count, object state)
    
    {
    
        var tcs = new TaskCompletionSource<int>();
    
        stream.BeginRead(buffer, offset, count, ar =>
    
        {
    
            try { tcs.SetResult(stream.EndRead; }
    
            catch (Exception exc) { tcs.SetException; }
    
        }, state);
    
        return tcs.Task;
    
    }
    

    混合方法。您也许发现手动达成 TAP 方式、但将落到实处中心逻辑委托给编写翻译器的这种办法很有用。 举个例子,当您想要验证编写翻译器生成的异步方法之外的实参时,恐怕必要选用这种混合方法,以便十分能够转义到该办法的第一手调用方而不是透过 System.Threading.Tasks.Task 对象被公开:

    public Task<int> MethodAsync(string input)
    
    {
    
        if (input == null) throw new ArgumentNullException("input");
    
        return MethodAsyncInternal;
    
    }
    
     
    
    private async Task<int> MethodAsyncInternal(string input)
    
    {
    
     
    
       // code that uses await goes here
    
     
    
       return value;
    
    }
    

    正文重要表明“使用编写翻译器”方法。


    3.TPL(基于职责的异步编程格局):这些就能用到使命并行库。

    EAP

    EAP是为了更便于处理UI的更新推出的模式,主要优点:它同Visual Studio UI设计器进行了很好的集成,可将大多数实现了EAP的类拖放到设计平面(design surface)上,双击控件对应的XXXCompleted事件名,会自动生成事件的回调方法,并将方法同事件自身联系起来。EAP保证事件在应用程序的GUI线程上引发,允许事件回调方法中的代码更新UI控件;
    
    EAP另一重要功能:支持EAP的类自动将应用程序模型映射到它的线程处理模型;EAP类在内部使用SynchronizationContext类。有的EAP类提供了取消、进度报告功能。
    

       FCL中只有15个项目完成了EAP方式,一般有一个XXXAsync方法和三个对应的XXXCompleted事件,以及这个艺术的一路版本:

    *       System.Object的派生类型:*

    *                  System.Activies.WorkflowInvoke  *

    *                  System.Deployment.Application.ApplicationDeployment*

    *                  System.Deployment.Application.InPlaceHosingManager*

    *                  System.Net.Mail.SmtpClient*

    *                  System.Net.PeerToPeer.PeerNameResolver*

    *                  System.Net.PeerToPeer.Collaboration.ContactManager*

    *                  System.Net.PeerToPeer.Collaboration.Peer*

    *                  System.Net.PeerToPeer.Collaboration.PeerContact*

    *                  System.Net.PeerToPeer.Collaboration.PeerNearMe*

    *                  System.ServiceModel.Activities.WorkflowControlClient*

    *                  System.ServiceModel.Discovery.AnnoucementClient*

    *                  System.ServiceModel.Discovery.DiscoveryClient*

    *      System.ComponentModel.Component的派生类型:*

                      System.ComponentModel.BackgroundWorker

                      System.Media.SoundPlay

                      System.Net.WebClient

                      System.Net.NetworkInformation.Ping

                      System.Windows.Forms.PictureBox(承接于Control类,Control类派生于Component类)

    private sealed class MyForm : System.Windows.Forms.Form {
        protected override void OnClick(EventArgs e) {
          // The System.Net.WebClient class supports the Event-based Asynchronous Pattern
          WebClient wc = new WebClient();
          // When a string completes downloading, the WebClient object raises the 
          // DownloadStringCompleted event which will invoke our ProcessString method         
          wc.DownloadStringCompleted  = ProcessString;
          // Start the asynchronous operation (this is like calling a BeginXxx method)
          wc.DownloadStringAsync(new Uri("http://Wintellect.com"));
          base.OnClick(e);
        }
        // This method is guaranteed to be called via the GUI thread
        private void ProcessString(Object sender, DownloadStringCompletedEventArgs e) {
          // If an error occurred, display it; else display the downloaded string
          System.Windows.Forms.MessageBox.Show((e.Error != null) ? e.Error.Message : e.Result);
          }
       }
    

    BackgroundWorker:唯有该类型用于可用来试行异步的测度范围的干活;提供多个事件:

    DoWork:向这几个事件登记的章程应该包括总结范围的代码。那一个事件由四个线程池线程调用RunWorkerAsync(多少个重载方法,带参的不二秘籍是向DoWork登记的不二等秘书诀的DoWork伊芙ntArgs参数对象的Argument属性传值,只可以在登记的格局中(如e.Argument)获取,Result属性必须设置成总计范围的操作希望重返的值)时引发;

    ProgressChanged:向那些事件登记的章程应该包蕴使用进度音讯来更新UI的代码。这些事件延续在GUI线程上引发。DoWork登记的艺术必须定时调用BackgroundWorker的ReportProgress方法来吸引ProgressChanged事件;

    RunWorkerCompleted:向那些事件登记的点子应该包蕴使用总计范围操作的结果对UI举行翻新的代码。那么些事件两次三番在GUI线程上引发。Result获取表示异步操作的结果;

    公家属性:CancellationPending(标志是还是不是已呼吁撤除后台操作)、IsBusy(标记是还是不是正在运维异步操作)、WorkReportsProgress(获取/设置是不是报告进程更新)、WorkerSupportsCancellation(获取/设置是不是协理异步撤消)

    国有措施:CancelAsync(请求裁撤挂起的后台操作)、ReportProgress、RunWorkerAsync

    异常

    不行不会抛出。在XXXCompleted事件管理方法中,必须询问AsyncCompleted伊夫ntArgs的Exception属性,看它是或不是null。假如不是null,就务须利用if语句推断Exception派生对象的种类,而不是应用catch块。

    编排异步方法


    C# 中 asyncawait 关键字是异步编制程序的着力。通过那三个至关心器重要字就足以轻易创立异步方法,大概与创立同步方法一样。如下所示的 WPF 程序,布局文件上有个按键和文本框:

    private async void StartButton_Click(object sender, RoutedEventArgs e)
    
    {
    
        // Call and await separately.
    
        //Task<int> getLengthTask = AccessTheWebAsync();
    
        //// You can do independent work here.
    
        //int contentLength = await getLengthTask;
    
     
    
        int contentLength = await AccessTheWebAsync();
    
     
    
        resultsTextBox.Text  =
    
            String.Format("rnLength of the downloaded string: {0}.rn", contentLength);
    
    }
    
     
    
     
    
    // Three things to note in the signature:
    
    //  - The method has an async modifier. 
    
    //  - The return type is Task or Task<T>. (See "Return Types" section.)
    
    //    Here, it is Task<int> because the return statement returns an integer.
    
    //  - The method name ends in "Async."
    
    async Task<int> AccessTheWebAsync()
    
    { 
    
        // You need to add a reference to System.Net.Http to declare client.
    
        HttpClient client = new HttpClient();
    
     
    
        // GetStringAsync returns a Task<string>. That means that when you await the
    
        // task you'll get a string (urlContents).
    
        Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
    
     
    
        // You can do work here that doesn't rely on the string from GetStringAsync.
    
        DoIndependentWork();
    
     
    
        // The await operator suspends AccessTheWebAsync.
    
        //  - AccessTheWebAsync can't continue until getStringTask is complete.
    
        //  - Meanwhile, control returns to the caller of AccessTheWebAsync.
    
        //  - Control resumes here when getStringTask is complete. 
    
        //  - The await operator then retrieves the string result from getStringTask.
    
        string urlContents = await getStringTask;
    
     
    
        // The return statement specifies an integer result.
    
        // Any methods that are awaiting AccessTheWebAsync retrieve the length value.
    
        return urlContents.Length;
    
    }
    
     
    
     
    
    void DoIndependentWork()
    
    {
    
        resultsTextBox.Text  = "Working . . . . . . .rn";
    
    }
    

    试行结果:

    Working . . . . . . .
    
     
    
    Length of the downloaded string: 41609.
    

    说明:

    1,当程序访问网络时,无论你怎么样拖拽、最大化最小化、怎样点击,UI 都不会错过响应;

    2,“async Task<int> AccessTheWebAsync()”方法签字,有三点要求小心:1)有 async 修饰符;2)重临类型是 TaskTask<int>。该格局是 Task<int>,因为它回到的是链接内容的大小;3)方法名以 Async 结尾;

    3,“string urlContents = await getStringTask;”语句,有四点必要小心:1)AccessTheWebAsync 方法直到 getStringTask 达成手艺延续;2)同时,调整流再次来到到 AccessTheWebAsync 的调用者;3)getStringTask 实现后,调节流才会回复;4)之后,await 操作符从 getStringTask 检索结果。

    上面总计让一个示范成为异步方法的性状:

    • 艺术签字包蕴三个 async 修饰符。
    • 遵从预订,异步方法的名目以“Async”后缀结尾。
    • 归来类型为下列项目之一:
      • 若是你的章程有 TResult 类型的回来语句,则为 Task<TResult>。
      • 假诺你的不二等秘书籍未有回去语句,则为 Task。
      • 假若您编写的是异步事件管理程序,则为 Void(Visual Basic 中为 Sub)。
    • 方法一般包蕴至少一个 await 表明式,该表明式标识贰个点,在该点上,直到等待的异步操作实现章程技巧延续。同临时间,将艺术挂起,并且控件重返到点子的调用方。

    在异步方法中,可使用提供的机要字和类型来提示须求做到的操作,且编写翻译器会落成其余操作,在那之中包罗不断追踪控件以挂起方法重返等待点时产生的景况。 一些例行流程(举例,循环和那几个管理)在观念异步代码中管理起来大概很艰巨。 在异步方法中,成分的编写制定频率与协助举办消除方案一样且此难题取得化解。

    编纂异步方法


    C# 中 asyncawait 关键字是异步编制程序的宗旨。通过那八个根本字就足以轻易创建异步方法,大致与创设同步方法同样。如下所示的 WPF 程序,布局文件上有个按键和文本框:

    private async void StartButton_Click(object sender, RoutedEventArgs e)
    
    {
    
        // Call and await separately.
    
        //Task<int> getLengthTask = AccessTheWebAsync();
    
        //// You can do independent work here.
    
        //int contentLength = await getLengthTask;
    
     
    
        int contentLength = await AccessTheWebAsync();
    
     
    
        resultsTextBox.Text  =
    
            String.Format("rnLength of the downloaded string: {0}.rn", contentLength);
    
    }
    
     
    
     
    
    // Three things to note in the signature:
    
    //  - The method has an async modifier. 
    
    //  - The return type is Task or Task<T>. (See "Return Types" section.)
    
    //    Here, it is Task<int> because the return statement returns an integer.
    
    //  - The method name ends in "Async."
    
    async Task<int> AccessTheWebAsync()
    
    { 
    
        // You need to add a reference to System.Net.Http to declare client.
    
        HttpClient client = new HttpClient();
    
     
    
        // GetStringAsync returns a Task<string>. That means that when you await the
    
        // task you'll get a string (urlContents).
    
        Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
    
     
    
        // You can do work here that doesn't rely on the string from GetStringAsync.
    
        DoIndependentWork();
    
     
    
        // The await operator suspends AccessTheWebAsync.
    
        //  - AccessTheWebAsync can't continue until getStringTask is complete.
    
        //  - Meanwhile, control returns to the caller of AccessTheWebAsync.
    
        //  - Control resumes here when getStringTask is complete. 
    
        //  - The await operator then retrieves the string result from getStringTask.
    
        string urlContents = await getStringTask;
    
     
    
        // The return statement specifies an integer result.
    
        // Any methods that are awaiting AccessTheWebAsync retrieve the length value.
    
        return urlContents.Length;
    
    }
    
     
    
     
    
    void DoIndependentWork()
    
    {
    
        resultsTextBox.Text  = "Working . . . . . . .rn";
    
    }
    

    实行结果:

    Working . . . . . . .
    
     
    
    Length of the downloaded string: 41609.
    

    说明:

    1,当程序访问互连网时,无论你怎么样拖拽、最大化最小化、怎么样点击,UI 都不会失掉响应;

    2,“async Task<int> AccessTheWebAsync()”方法具名,有三点必要留意:1)有 async 修饰符;2)重临类型是 TaskTask<int>。该办法是 Task<int>,因为它回到的是链接内容的高低;3)方法名以 Async 结尾;

    3,“string urlContents = await getStringTask;”语句,有四点供给注意:1)AccessTheWebAsync 方法直到 getStringTask 完成本事继续;2)同不时候,调节流再次来到到 AccessTheWebAsync 的调用者;3)getStringTask 完结后,调节流才会还原;4)之后,await 操作符从 getStringTask 检索结果。

    上面总括让贰个示范成为异步方法的特点:

    • 办法签字包蕴二个 async 修饰符。
    • 遵纪守法约定,异步方法的称呼以“Async”后缀结尾。
    • 回来类型为下列项目之一:
      • 假使您的主意有 TResult 类型的归来语句,则为 Task<TResult>。
      • 假定你的措施未有回来语句,则为 Task。
      • 假使您编写的是异步事件管理程序,则为 Void(Visual Basic 中为 Sub)。
    • 主意一般包括至少多少个 await 表明式,该表明式标识八个点,在该点上,直到等待的异步操作实现章程技术承袭。同一时间,将艺术挂起,并且控件重临到方式的调用方。

    在异步方法中,可应用提供的重大字和品种来提示须要产生的操作,且编写翻译器会成功其余操作,个中包罗不断追踪控件以挂起方法重返等待点时产生的景况。 一些健康流程(举例,循环和极其管理)在古板异步代码中管理起来恐怕很拮据。 在异步方法中,成分的编辑频率与联合消除方案相同且此难题取得缓慢解决。

    1.1 简介

    在此前的多少个章节中,就线程的施用和二十多线程相关的从头到尾的经过进行了介绍。因为线程涉及到异步、同步、十分传递等主题材料,所以在品种中采用多线程的代价是相比较高昂的,要求编写制定大批量的代码来达到科学和健壮性。

    为了消除那样局地的标题,在.Net Framework 4.0中引进了一个关于一步操作的API。它称为任务并行库(Task Parallel Library)。然后在.Net Framwork 4.5中对它进行了轻微的创新,本文的案例都以用风尚版本的TPL库,而且大家还足以使用C# 5.0的新特点await/async来简化TAP编制程序,当然那是随后才介绍的。

    TPL内部采纳了线程池,然而功效更加高。在把线程归还回线程池在此之前,它会在同一线程中逐一实践多少Task,那样幸免了部分小任务上下文切换浪费时间片的难题。

    职务是指标,个中封装了以异步格局实行的做事,不过委托也是包裹了代码的靶子。职责和嘱托的区别在于,委托是同步的,而职责是异步的。

    在本章中,我们将商谈谈哪些行使TPL库来张开职务之间的重组同步,怎么样将残留的APM和EAP格局转变为TPL方式等等。

     

    TAP

    .NET4.0 中引进了新的异步编制程序模型“基于任务的异步编制程序模型(TAP)”,并且推荐我们在开拓新的二十八线程应用程序中首荐TAP,在.NET4.5中尤为对TPL库举行了大气的优化与改进(async和await)。那未来自己先介绍下TAP具备啥样优势:

    1. 任务调解器(TaskScheduler)注重于底层的线程池引擎,可自定义一个TaskScheduler改换调解算法,同一时候不转移代码或编程模型。通过有个别队列的职责内联化(task inlining)和办事窃取(work-stealing)机制而发起了多量职务,Task可认为我们升高程序品质。
    2. 能够应用PreferFairness标识,获取与ThreadPool.QueueUserWorkItem或然一个寄托的BeginInvoke一样的线程池行为。

            3.  自由自在完成职务等待、职务裁撤、一而再职分、格外管理(System.AggregateException)、GUI线程操作。

           4.  在职分运转后,能够随时以职分一连的款型登记回调。

           5.  丰硕利用现存的线程,幸免创造不须要的额外线程。

           6.  结合C#5.0引进async和await关键字轻巧实现“异步方法”。

    APM转换为TAP:

    运用TaskFactory的FromAsync方法,传递八个实参:BeginXxx方法、EndXxx方法、Object状态、可选的TaskCreationOptions值,重临对贰个Task对象的引用;

    private static void ConvertingApmToTask() {
          // Instead of this:
          WebRequest webRequest = WebRequest.Create("http://Wintellect.com/");
          webRequest.BeginGetResponse(result => {
             WebResponse webResponse = null;
             try {
                webResponse = webRequest.EndGetResponse(result);
                Console.WriteLine("Content length: "   webResponse.ContentLength);
             }
             catch (WebException we) {
                Console.WriteLine("Failed: "   we.GetBaseException().Message);
             }
             finally {
                if (webResponse != null) webResponse.Close();
             }
          }, null);
          Console.ReadLine();  // for testing purposes
          // Make a Task from an async operation that FromAsync starts
          webRequest = WebRequest.Create("http://Wintellect.com/");
          var t1 = Task.Factory.FromAsync<WebResponse>(webRequest.BeginGetResponse, webRequest.EndGetResponse, null, TaskCreationOptions.None);
          var t2 = t1.ContinueWith(task => {
             WebResponse webResponse = null;
             try {
                webResponse = task.Result;
                Console.WriteLine("Content length: "   webResponse.ContentLength);
             }
             catch (AggregateException ae) {
                if (ae.GetBaseException() is WebException)
                   Console.WriteLine("Failed: "   ae.GetBaseException().Message);
                else throw;
             }
             finally { if (webResponse != null) webResponse.Close(); }
          });
          try {t2.Wait();  // for testing purposes only}
          catch (AggregateException) { }
       }
    

    EAP转换成TAP:

    应用System.Threading.Tasks.TaskCompletionSource类进行打包;

    图片 6

    当组织贰个TaskCompletionSource对象,也会转移七个Task,可透过其Task属性获取;当三个异步操作达成时,它采纳TaskCompletionSource对象来安装它因为啥而成功,撤销,未管理的足够只怕它的结果。调用某些SetXxx方法,能够安装底层Task对象的场所。

    private sealed class MyFormTask : System.Windows.Forms.Form {
          protected override void OnClick(EventArgs e) {
             // The System.Net.WebClient class supports the Event-based Asynchronous Pattern
             WebClient wc = new WebClient();
             // Create the TaskCompletionSource and its underlying Task object
             var tcs = new TaskCompletionSource<String>();
             // When a string completes downloading, the WebClient object raises the 
             // DownloadStringCompleted event which will invoke our ProcessString method
             wc.DownloadStringCompleted  = (sender, ea) => {
                // This code always executes on the GUI thread; set the Task’s state
                if (ea.Cancelled) tcs.SetCanceled();
                else if (ea.Error != null) tcs.SetException(ea.Error);
                else tcs.SetResult(ea.Result);
             };
             // Have the Task continue with this Task that shows the result in a message box
    // NOTE: The TaskContinuationOptions.ExecuteSynchronously flag is required to have this code
             // run on the GUI thread; without the flag, the code runs on a thread pool thread 
             tcs.Task.ContinueWith(t => {
                try { System.Windows.Forms.MessageBox.Show(t.Result);}
                catch (AggregateException ae) {
                   System.Windows.Forms.MessageBox.Show(ae.GetBaseException().Message);
                }
             }, TaskContinuationOptions.ExecuteSynchronously);
             // Start the asynchronous operation (this is like calling a BeginXxx method)
             wc.DownloadStringAsync(new Uri("http://Wintellect.com"));
             base.OnClick(e);
          }
       }
    

    实现了TAP的类:存在XxxTaskAsync的方法, 帮助异步操作的撤废和速度的告诉的机能;

    撤除:能够经过同盟式撤消格局,向异步方法传入CancellationToken 参数,通过调用其ThrowIfCancellationRequested方法来定期检查操作是或不是曾经撤除;

    进度报告:可以由此IProgress<T>接口来贯彻速度报告的效用;

    履新GUI: TaskScheduler.FromCurrentSynchronizationContext()获取同步上下文职责调解器,将关乎该对象的装有职责都调解给GUI线程,使职责代码能不负众望更新UI;

    private sealed class MyForm : System.Windows.Forms.Form {
            public MyForm() {
                Text = "Synchronization Context Task Scheduler Demo";
                Visible = true; Width = 400; Height = 100;
            }
             private static Int32 Sum(CancellationToken ct, Int32 n) {
            Int32 sum = 0;
            for (; n > 0; n--) {
                // The following line throws OperationCanceledException when Cancel 
                // is called on the CancellationTokenSource referred to by the token
                ct.ThrowIfCancellationRequested();
                //Thread.Sleep(0);   // Simulate taking a long time
                checked { sum  = n; }
            }
            return sum;
           }
            private readonly TaskScheduler m_syncContextTaskScheduler =
               TaskScheduler.FromCurrentSynchronizationContext();
            private CancellationTokenSource m_cts;
            protected override void OnMouseClick(System.Windows.Forms.MouseEventArgs e) {
                if (m_cts != null) {    // An operation is in flight, cancel it
                    m_cts.Cancel();
                    m_cts = null;
                } else {                // An operation is not in flight, start it
                    Text = "Operation running";
                    m_cts = new CancellationTokenSource();
               // This task uses the default task scheduler and executes on a thread pool thread
                    var t = new Task<Int32>(() => Sum(m_cts.Token, 20000), m_cts.Token);
                    t.Start();
     // These tasks use the synchronization context task scheduler and execute on the GUI thread
                    t.ContinueWith(task => Text = "Result: "   task.Result,
                       CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion,
                       m_syncContextTaskScheduler);
                    t.ContinueWith(task => Text = "Operation canceled",
                       CancellationToken.None, TaskContinuationOptions.OnlyOnCanceled,
                       m_syncContextTaskScheduler);
                    t.ContinueWith(task => Text = "Operation faulted",
                       CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted,
                       m_syncContextTaskScheduler);
                }
                base.OnMouseClick(e);
            }
    }
    

    足够管理

    在职责抛出的未管理至极都封装在System.AggregateException对象中。那几个目的会蕴藏在章程再次回到的Task或Task<TResult>对象中,须求通过走访Wait()、Result、Exception成员技巧体察到充分。(所以,在做客Result在此以前,应先观望IsCanceled和IsFaulted属性)

    借使一直不访问Task的Wait()、Result、Exception成员,那么您将永生长久注意不到这几个极度的产生。为了扶助您检验到那个未管理的丰盛,能够向TaskScheduler对象的UnobservedTaskException事件注册回调函数。每当三个Task被垃圾回收时,假使存在二个未有在意到的要命,CL奇骏的利落器线程会引发这一个事件。

    可在事变回调函数中调用UnobservedTaskException伊芙ntArgs对象的SetObserved() 方法来提议已经管理好了老大,从而阻碍CL奥德赛终止线程。然则并不推荐这么做,宁愿终止进度也无须带着曾经毁损的处境继续运行。

    异步程序中的控制流


    异步编制程序中最需弄清的是调整流是哪些从章程移动到艺术。

    private async void StartButton_Click(object sender, RoutedEventArgs e)
    
           {
    
               // Call and await separately.
    
               //Task<int> getLengthTask = AccessTheWebAsync();
    
               //// You can do independent work here.
    
               //int contentLength = await getLengthTask;
    
               resultsTextBox.Text  = "1:  Entering startButton_Click.rn"  
    
                   "           Calling AccessTheWebAsync.rn";
    
     
    
               int contentLength = await AccessTheWebAsync();
    
     
    
               resultsTextBox.Text  =
    
                   String.Format("rn6:   Length of the downloaded string: {0}.rn", contentLength);
    
           }
    
     
    
           async Task<int> AccessTheWebAsync()
    
           {
    
               resultsTextBox.Text  = "rn2:  Entering AccessTheWebAsync.";
    
     
    
               HttpClient client = new HttpClient();
    
     
    
               resultsTextBox.Text  = "rn        Calling HttpClient.GetStringAsync.rn";
    
     
    
               Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
    
     
    
               DoIndependentWork();
    
     
    
               resultsTextBox.Text  = "rn4:  Back in startButton_Click.rn"  
    
                   "       Task getStringTask is started.rn";
    
               string urlContents = await getStringTask;
    
     
    
               resultsTextBox.Text  = "rn5:  Back in AccessTheWebAsync."  
    
                   "rn       Task getStringTask is complete."  
    
                   "rn       Processing the return statement."  
    
                   "rn       Exiting from AccessTheWebAsync.rn";
    
     
    
               return urlContents.Length;
    
           }
    
     
    
     
    
           void DoIndependentWork()
    
           {
    
               resultsTextBox.Text  = "rn3:  Entering DoIndependentWork.rn";
    
     
    
               resultsTextBox.Text  = "rn        Working . . . . . . .rn";
    
           }
    

    运营结果:

    1:  Entering startButton_Click.
    
               Calling AccessTheWebAsync.
    
     
    
    2:  Entering AccessTheWebAsync.
    
            Calling HttpClient.GetStringAsync.
    
     
    
    3:  Entering DoIndependentWork.
    
     
    
            Working . . . . . . .
    
     
    
    4:  Back in startButton_Click.
    
           Task getStringTask is started.
    
     
    
    5:  Back in AccessTheWebAsync.
    
           Task getStringTask is complete.
    
           Processing the return statement.
    
           Exiting from AccessTheWebAsync.
    
     
    
    6:   Length of the downloaded string: 41609.
    

    再稍加复杂点:

    private async void startButton_Click(object sender, RoutedEventArgs e)
    
           {
    
               // The display lines in the example lead you through the control shifts.
    
               resultsTextBox.Text  = "ONE:   Entering startButton_Click.rn"  
    
                   "           Calling AccessTheWebAsync.rn";
    
     
    
               Task<int> getLengthTask = AccessTheWebAsync();
    
     
    
               resultsTextBox.Text  = "rnFOUR:  Back in startButton_Click.rn"  
    
                   "           Task getLengthTask is started.rn"  
    
                   "           About to await getLengthTask -- no caller to return to.rn";
    
     
    
               int contentLength = await getLengthTask;
    
     
    
               resultsTextBox.Text  = "rnSIX:   Back in startButton_Click.rn"  
    
                   "           Task getLengthTask is finished.rn"  
    
                   "           Result from AccessTheWebAsync is stored in contentLength.rn"  
    
                   "           About to display contentLength and exit.rn";
    
     
    
               resultsTextBox.Text  =
    
                   String.Format("rnLength of the downloaded string: {0}.rn", contentLength);
    
           }
    
     
    
           async Task<int> AccessTheWebAsync()
    
           {
    
               resultsTextBox.Text  = "rnTWO:   Entering AccessTheWebAsync.";
    
     
    
               // Declare an HttpClient object and increase the buffer size. The default
    
               // buffer size is 65,536.
    
               HttpClient client =
    
                   new HttpClient() { MaxResponseContentBufferSize = 1000000 };
    
     
    
               resultsTextBox.Text  = "rn           Calling HttpClient.GetStringAsync.rn";
    
     
    
               // GetStringAsync returns a Task<string>. 
    
               Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
    
     
    
               resultsTextBox.Text  = "rnTHREE: Back in AccessTheWebAsync.rn"  
    
                   "           Task getStringTask is started.";
    
     
    
               // AccessTheWebAsync can continue to work until getStringTask is awaited.
    
     
    
               resultsTextBox.Text  =
    
                   "rn           About to await getStringTask and return a Task<int> to startButton_Click.rn";
    
     
    
               // Retrieve the website contents when task is complete.
    
               string urlContents = await getStringTask;
    
     
    
               resultsTextBox.Text  = "rnFIVE:  Back in AccessTheWebAsync."  
    
                   "rn           Task getStringTask is complete."  
    
                   "rn           Processing the return statement."  
    
                   "rn           Exiting from AccessTheWebAsync.rn";
    
     
    
               return urlContents.Length;
    
           }
    

    运行结果:

    ONE:   Entering startButton_Click.
    
               Calling AccessTheWebAsync.
    
     
    
    TWO:   Entering AccessTheWebAsync.
    
               Calling HttpClient.GetStringAsync.
    
     
    
    THREE: Back in AccessTheWebAsync.
    
               Task getStringTask is started.
    
               About to await getStringTask and return a Task<;int> to startButton_Click.
    
     
    
    FOUR:  Back in startButton_Click.
    
               Task getLengthTask is started.
    
               About to await getLengthTask -- no caller to return to.
    
     
    
    FIVE:  Back in AccessTheWebAsync.
    
               Task getStringTask is complete.
    
               Processing the return statement.
    
               Exiting from AccessTheWebAsync.
    
     
    
    SIX:   Back in startButton_Click.
    
               Task getLengthTask is finished.
    
               Result from AccessTheWebAsync is stored in contentLength.
    
               About to display contentLength and exit.
    
     
    
    Length of the downloaded string: 41635.
    

    异步程序中的调节流


    异步编程中最需弄清的是调控流是什么样从事艺术工作术移动到格局。

    private async void StartButton_Click(object sender, RoutedEventArgs e)
    
           {
    
               // Call and await separately.
    
               //Task<int> getLengthTask = AccessTheWebAsync();
    
               //// You can do independent work here.
    
               //int contentLength = await getLengthTask;
    
               resultsTextBox.Text  = "1:  Entering startButton_Click.rn"  
    
                   "           Calling AccessTheWebAsync.rn";
    
     
    
               int contentLength = await AccessTheWebAsync();
    
     
    
               resultsTextBox.Text  =
    
                   String.Format("rn6:   Length of the downloaded string: {0}.rn", contentLength);
    
           }
    
     
    
           async Task<int> AccessTheWebAsync()
    
           {
    
               resultsTextBox.Text  = "rn2:  Entering AccessTheWebAsync.";
    
     
    
               HttpClient client = new HttpClient();
    
     
    
               resultsTextBox.Text  = "rn        Calling HttpClient.GetStringAsync.rn";
    
     
    
               Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
    
     
    
               DoIndependentWork();
    
     
    
               resultsTextBox.Text  = "rn4:  Back in startButton_Click.rn"  
    
                   "       Task getStringTask is started.rn";
    
               string urlContents = await getStringTask;
    
     
    
               resultsTextBox.Text  = "rn5:  Back in AccessTheWebAsync."  
    
                   "rn       Task getStringTask is complete."  
    
                   "rn       Processing the return statement."  
    
                   "rn       Exiting from AccessTheWebAsync.rn";
    
     
    
               return urlContents.Length;
    
           }
    
     
    
     
    
           void DoIndependentWork()
    
           {
    
               resultsTextBox.Text  = "rn3:  Entering DoIndependentWork.rn";
    
     
    
               resultsTextBox.Text  = "rn        Working . . . . . . .rn";
    
           }
    

    运转结果:

    1:  Entering startButton_Click.
    
               Calling AccessTheWebAsync.
    
     
    
    2:  Entering AccessTheWebAsync.
    
            Calling HttpClient.GetStringAsync.
    
     
    
    3:  Entering DoIndependentWork.
    
     
    
            Working . . . . . . .
    
     
    
    4:  Back in startButton_Click.
    
           Task getStringTask is started.
    
     
    
    5:  Back in AccessTheWebAsync.
    
           Task getStringTask is complete.
    
           Processing the return statement.
    
           Exiting from AccessTheWebAsync.
    
     
    
    6:   Length of the downloaded string: 41609.
    

    再稍加复杂点:

    private async void startButton_Click(object sender, RoutedEventArgs e)
    
           {
    
               // The display lines in the example lead you through the control shifts.
    
               resultsTextBox.Text  = "ONE:   Entering startButton_Click.rn"  
    
                   "           Calling AccessTheWebAsync.rn";
    
     
    
               Task<int> getLengthTask = AccessTheWebAsync();
    
     
    
               resultsTextBox.Text  = "rnFOUR:  Back in startButton_Click.rn"  
    
                   "           Task getLengthTask is started.rn"  
    
                   "           About to await getLengthTask -- no caller to return to.rn";
    
     
    
               int contentLength = await getLengthTask;
    
     
    
               resultsTextBox.Text  = "rnSIX:   Back in startButton_Click.rn"  
    
                   "           Task getLengthTask is finished.rn"  
    
                   "           Result from AccessTheWebAsync is stored in contentLength.rn"  
    
                   "           About to display contentLength and exit.rn";
    
     
    
               resultsTextBox.Text  =
    
                   String.Format("rnLength of the downloaded string: {0}.rn", contentLength);
    
           }
    
     
    
           async Task<int> AccessTheWebAsync()
    
           {
    
               resultsTextBox.Text  = "rnTWO:   Entering AccessTheWebAsync.";
    
     
    
               // Declare an HttpClient object and increase the buffer size. The default
    
               // buffer size is 65,536.
    
               HttpClient client =
    
                   new HttpClient() { MaxResponseContentBufferSize = 1000000 };
    
     
    
               resultsTextBox.Text  = "rn           Calling HttpClient.GetStringAsync.rn";
    
     
    
               // GetStringAsync returns a Task<string>. 
    
               Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
    
     
    
               resultsTextBox.Text  = "rnTHREE: Back in AccessTheWebAsync.rn"  
    
                   "           Task getStringTask is started.";
    
     
    
               // AccessTheWebAsync can continue to work until getStringTask is awaited.
    
     
    
               resultsTextBox.Text  =
    
                   "rn           About to await getStringTask and return a Task<int> to startButton_Click.rn";
    
     
    
               // Retrieve the website contents when task is complete.
    
               string urlContents = await getStringTask;
    
     
    
               resultsTextBox.Text  = "rnFIVE:  Back in AccessTheWebAsync."  
    
                   "rn           Task getStringTask is complete."  
    
                   "rn           Processing the return statement."  
    
                   "rn           Exiting from AccessTheWebAsync.rn";
    
     
    
               return urlContents.Length;
    
           }
    

    运作结果:

    ONE:   Entering startButton_Click.
    
               Calling AccessTheWebAsync.
    
     
    
    TWO:   Entering AccessTheWebAsync.
    
               Calling HttpClient.GetStringAsync.
    
     
    
    THREE: Back in AccessTheWebAsync.
    
               Task getStringTask is started.
    
               About to await getStringTask and return a Task<;int> to startButton_Click.
    
     
    
    FOUR:  Back in startButton_Click.
    
               Task getLengthTask is started.
    
               About to await getLengthTask -- no caller to return to.
    
     
    
    FIVE:  Back in AccessTheWebAsync.
    
               Task getStringTask is complete.
    
               Processing the return statement.
    
               Exiting from AccessTheWebAsync.
    
     
    
    SIX:   Back in startButton_Click.
    
               Task getLengthTask is finished.
    
               Result from AccessTheWebAsync is stored in contentLength.
    
               About to display contentLength and exit.
    
     
    
    Length of the downloaded string: 41635.
    

    1.2 创立任务

    在本节中,首借使出现说法了什么创设几个任务。其主要行使了System.Threading.Tasks命名空间下的Task类。该类能够被实例化并且提供了一组静态方法,能够方便急速的创设职务。

    在底下实例代码中,分别延时了二种布满的职务成立格局,并且创办职责是足以内定职责创设的选项,从而达成最优的创办方式。

    TaskCreationOptions中一共有7个枚举,枚举是足以应用|运算符组合定义的。其枚举如下表所示。

    成员名称 说明
    AttachedToParent 指定将任务附加到任务层次结构中的某个父级。 默认情况下,子任务(即由外部任务创建的内部任务)将独立于其父任务执行。 可以使用 TaskContinuationOptions.AttachedToParent 选项以便将父任务和子任务同步。请注意,如果使用 DenyChildAttach 选项配置父任务,则子任务中的 AttachedToParent 选项不起作用,并且子任务将作为分离的子任务执行。有关详细信息,请参阅附加和分离的子任务
    DenyChildAttach 指定任何尝试作为附加的子任务执行(即,使用 AttachedToParent 选项创建)的子任务都无法附加到父任务,会改成作为分离的子任务执行。 有关详细信息,请参阅附加和分离的子任务
    HideScheduler 防止环境计划程序被视为已创建任务的当前计划程序。 这意味着像 StartNew 或 ContinueWith 创建任务的执行操作将被视为 Default 当前计划程序。
    LongRunning 指定任务将是长时间运行的、粗粒度的操作,涉及比细化的系统更少、更大的组件。 它会向 TaskScheduler 提示,过度订阅可能是合理的。 可以通过过度订阅创建比可用硬件线程数更多的线程。 它还将提示任务计划程序:该任务需要附加线程,以使任务不阻塞本地线程池队列中其他线程或工作项的向前推动。
    None 指定应使用默认行为。
    PreferFairness 提示 TaskScheduler 以一种尽可能公平的方式安排任务,这意味着较早安排的任务将更可能较早运行,而较晚安排运行的任务将更可能较晚运行。
    RunContinuationsAsynchronously 强制异步执行添加到当前任务的延续任务。请注意,RunContinuationsAsynchronously 成员在以 .NET Framework 4.6 开头的 TaskCreationOptions 枚举中可用。
    static void Main(string[] args)
    {
        // 使用构造方法创建任务
        var t1 = new Task(() => TaskMethod("Task 1"));
        var t2 = new Task(() => TaskMethod("Task 2"));
    
        // 需要手动启动
        t2.Start();
        t1.Start();
    
        // 使用Task.Run 方法启动任务  不需要手动启动
        Task.Run(() => TaskMethod("Task 3"));
    
        // 使用 Task.Factory.StartNew方法 启动任务 实际上就是Task.Run
        Task.Factory.StartNew(() => TaskMethod("Task 4"));
    
        // 在StartNew的基础上 添加 TaskCreationOptions.LongRunning 告诉 Factory该任务需要长时间运行
        // 那么它就会可能会创建一个 非线程池线程来执行任务  
        Task.Factory.StartNew(() => TaskMethod("Task 5"), TaskCreationOptions.LongRunning);
    
        ReadLine();
    }
    
    static void TaskMethod(string name)
    {
        WriteLine($"任务 {name} 运行,线程 id {CurrentThread.ManagedThreadId}. 是否为线程池线程: {CurrentThread.IsThreadPoolThread}.");
    }
    

    运作结果如下图所示。

    图片 7

    4.1 简介

    Async /Await

    在.NET Framework 4.0中增多.NET Framework 4.5中新的异步操作库(async/await),该包由五个库组成:Microsoft.Bcl、Microsoft.Bcl.Async和Microsoft.Bcl.Build。

    Install-Package Microsoft.Bcl.Async

    注:asp.net 框架必须要升级.net framework框架技能运用 async/await

    一经不行消息是“Message : Could not load file or assembly 'System.Core, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, Retargetable=Yes' or one of its dependencies. The given assembly name or codebase was invalid. (Exception from HRESULT: 0x80131047)”,

    那须要你去微软官方网站下载.net4.0的KB2468871补丁来安装。

    C# 5引入了异步函数(asynchrnous function)的概念。常常是指用async修饰符注明的,可

    包罗await表明式的模式或无名函数;

    async关键字创设了二个状态机,类似于yield return语句;await关键字只好用来有用async修饰符注明的章程。async修饰符只可以用于再次来到Task/Task<TResult>或void的艺术。await只可以用来调用重临Task/Task<TResult>的不二等秘书籍;await会解除线程的封堵,完结调用的任务;等待任务完毕后,获取结果,然后实行await关键字背后的代码;编写翻译器会把await的表明式后的代码应用 Task.ContinueWith 包裹了四起,回调时暗许使用当前线程的一块儿上下文职务调解器;借使不利用同一的联合上下文,必须调用Task实例的ConfigureAwait(false)方法;

    await msg.Content.ReadAsStringAsync().ConfigureAwait(false);

    异步方法的扬言语法与其余办法千篇一律,只是要包括async上下文关键字。async能够出

    到现在归来类型在此以前的别的岗位。async修饰符在调换的代码中并未有效果,也可省略不写,它总之表明了您的意料,告诉编译器可以积极搜索await表明式,也得以搜索应该转变到异步调用和await表明式的块调用。

    调用者和异步方法之间是经过重返值来通讯的。异步函数的回来类型只可以为:

    Void 、Task、Task<TResult>;Task和Task<TResult>类型都意味八个或然还未造成的操作。 Task<TResult>承接自Task。二者的分别是,Task<TResult>表示二个再次回到值为T类型的操作,而Task则无需发出再次来到值。在某种意义上,你可以以为Task正是Task<void>类型;

    所以将异步方法设计为能够回来void,是为了和事件管理程序包容。

    异步方法具名的封锁:全数参数都不能够利用out或ref修饰符。

    图片 8

     

    API 异步方法


    怎样找到像 GetStringAsync 这样协助异步编程的不二等秘书籍。 .NET Framework 4.5 包蕴使用 async 和 await 的无数分子,它们都已“Async”为后缀和 Task 或 Task<TResult> 的归来类型。 比如,System.IO.Stream 类包涵的措施 CopyToAsync、ReadAsync、WriteAsync 等格局以及一起方法 CopyTo、Read 和 Write。

    API 异步方法


    什么找到像 GetStringAsync 那样支持异步编制程序的办法。 .NET Framework 4.5 蕴含使用 async 和 await 的多数成员,它们都已“Async”为后缀和 Task 或 Task<TResult> 的回到类型。 举个例子,System.IO.Stream 类包罗的情势CopyToAsync、ReadAsync、WriteAsync 等艺术以及一齐方法 CopyTo、Read 和 Write。

    1.3 使用职分奉行基本的操作

    在本节中,使用职务执行基本的操作,并且获得义务实施到位后的结果值。本节内容对比轻易,在此不做过多介绍。

    示范代码如下,在主线程中要获得结果值,常用的办法正是访问task.Result属性,假设职务线程还没实践达成,那么会阻塞主线程,直到线程实践完。借使任务线程试行达成,那么将一向获得运算的结果值。

    Task 3中,使用了task.Status来打字与印刷线程的景况,线程每种景况的求实意思,将要下一节中介绍。

    static void Main(string[] args)
    {
        // 直接执行方法 作为参照
        TaskMethod("主线程任务");
    
        // 访问 Result属性 达到运行结果
        Task<int> task = CreateTask("Task 1");
        task.Start();
        int result = task.Result;
        WriteLine($"运算结果: {result}");
    
        // 使用当前线程,同步执行任务
        task = CreateTask("Task 2");
        task.RunSynchronously();
        result = task.Result;
        WriteLine($"运算结果:{result}");
    
        // 通过循环等待 获取运行结果
        task = CreateTask("Task 3");
        WriteLine(task.Status);
        task.Start();
    
        while (!task.IsCompleted)
        {
            WriteLine(task.Status);
            Sleep(TimeSpan.FromSeconds(0.5));
        }
    
        WriteLine(task.Status);
        result = task.Result;
        WriteLine($"运算结果:{result}");
    
        Console.ReadLine();
    }
    
    static Task<int> CreateTask(string name)
    {
        return new Task<int>(() => TaskMethod(name));
    }
    
    static int TaskMethod(string name)
    {
        WriteLine($"{name} 运行在线程 {CurrentThread.ManagedThreadId}上. 是否为线程池线程 {CurrentThread.IsThreadPoolThread}");
    
        Sleep(TimeSpan.FromSeconds(2));
    
        return 42;
    }
    

    运维结果如下,可见Task 1Task 2均是运维在主线程上,并非线程池线程。

    图片 9

     

    线程


    异步方法意在成为非阻止操作。异步方法中的 await 表明式在等待的职分正在运营时,不会阻碍当前线程。相反,表达式在此伏彼起时,注册格局的其他部分并将控件重回到异步方法的调用方。

    async 和 await 关键字不会促成创造其余线程。因为异步方法不会在其自身线程上运转,因而它不必要三十二线程。 唯有当方法处于活动状态时,该办法就要此时此刻联手上下文中运转并动用线程上的年华。 能够采纳 Task.Run 将占用大量 CPU 的专门的工作移到后台线程,可是后台线程不会帮助正在守候结果的进程变为可用状态。

    对于异步编制程序而言,该基于异步的法门优于大概各个用例中的现成措施。 具体来说,此措施比 BackgroundWorker 更适用于 IO 绑定的操作,因为此代码更简便易行且无需防卫争用规范。 结合 Task.Run 使用时,异步编程比 BackgroundWorker 更适用于 CPU 绑定的操作,因为异步编制程序将运营代码的和睦细节与 Task.Run 传输至线程池的办事分别开来。

    线程


    异步方法意在成为非阻止操作。异步方法中的 await 表明式在守候的职责正在周转时,不会堵住当前线程。相反,表明式在继续时,注册方式的别的部分并将控件重临到异步方法的调用方。

    async 和 await 关键字不会导致创制其余线程。因为异步方法不会在其自己线程上运转,由此它无需三十二线程。 唯有当方法处于活动状态时,该格局就要当下联合签名上下文中运维并运用线程上的小时。 可以行使 Task.Run 将占用多量 CPU 的做事移到后台线程,可是后台线程不会赞助正在等待结果的历程变为可用状态。

    对于异步编制程序来讲,该基于异步的方式优于差相当少各样用例中的现存措施。 具体来说,此方法比 BackgroundWorker 更适用于 IO 绑定的操作,因为此代码更简约且无需防御争用规范化。 结合 Task.Run 使用时,异步编制程序比 BackgroundWorker 更适用于 CPU 绑定的操作,因为异步编制程序将运转代码的协调细节与 Task.Run 传输至线程池的干活分别开来。

    1.4 组合职分

    在本节中,呈现了职分之中贰个强硬的意义,那正是构成职分。通过整合职务可很好的讲述职责与职分之间的异步、同步关系,大大下落了编制程序的难度。

    结合任务主假若透过task.ContinueWith()task.WhenAny()task.WhenAll()等和task.GetAwaiter().OnCompleted()主意来贯彻。

    在使用task.ContinueWith()艺术时,需求小心它也可传递一多种的枚举选项TaskContinuationOptions,该枚举选项和TaskCreationOptions类似,其具体定义如下表所示。

    成员名称 说明
    AttachedToParent 如果延续为子任务,则指定将延续附加到任务层次结构中的父级。 只有当延续前面的任务也是子任务时,延续才可以是子任务。 默认情况下,子任务(即由外部任务创建的内部任务)将独立于其父任务执行。 可以使用 TaskContinuationOptions.AttachedToParent 选项以便将父任务和子任务同步。请注意,如果使用 DenyChildAttach 选项配置父任务,则子任务中的 AttachedToParent 选项不起作用,并且子任务将作为分离的子任务执行。有关更多信息,请参见Attached and Detached Child Tasks
    DenyChildAttach 指定任何使用 TaskCreationOptions.AttachedToParent 选项创建,并尝试作为附加的子任务执行的子任务(即,由此延续创建的任何嵌套内部任务)都无法附加到父任务,会改成作为分离的子任务执行。 有关详细信息,请参阅附加和分离的子任务
    ExecuteSynchronously 指定应同步执行延续任务。 指定此选项后,延续任务在导致前面的任务转换为其最终状态的相同线程上运行。如果在创建延续任务时已经完成前面的任务,则延续任务将在创建此延续任务的线程上运行。 如果前面任务的 CancellationTokenSource 已在一个 finally(在 Visual Basic 中为 Finally)块中释放,则使用此选项的延续任务将在该 finally 块中运行。 只应同步执行运行时间非常短的延续任务。由于任务以同步方式执行,因此无需调用诸如 Task.Wait 的方法来确保调用线程等待任务完成。
    HideScheduler 指定由延续通过调用方法(如 Task.RunTask.ContinueWith)创建的任务将默认计划程序 (TaskScheduler.Default) 视为当前的计划程序,而不是正在运行该延续的计划程序。
    LazyCancellation 在延续取消的情况下,防止延续的完成直到完成先前的任务。
    LongRunning 指定延续将是长期运行的、粗粒度的操作。 它会向 TaskScheduler 提示,过度订阅可能是合理的。
    None 如果未指定延续选项,应在执行延续任务时使用指定的默认行为。 延续任务在前面的任务完成后以异步方式运行,与前面任务最终的 Task.Status 属性值无关。 如果延续为子任务,则会将其创建为分离的嵌套任务。
    NotOnCanceled 指定不应在延续任务前面的任务已取消的情况下安排延续任务。 如果前面任务完成的 Task.Status 属性是 TaskStatus.Canceled,则前面的任务会取消。 此选项对多任务延续无效。
    NotOnFaulted 指定不应在延续任务前面的任务引发了未处理异常的情况下安排延续任务。 如果前面任务完成的 Task.Status 属性是 TaskStatus.Faulted,则前面的任务会引发未处理的异常。 此选项对多任务延续无效。
    NotOnRanToCompletion 指定不应在延续任务前面的任务已完成运行的情况下安排延续任务。 如果前面任务完成的 Task.Status 属性是 TaskStatus.RanToCompletion,则前面的任务会运行直至完成。 此选项对多任务延续无效。
    OnlyOnCanceled 指定只应在延续前面的任务已取消的情况下安排延续任务。 如果前面任务完成的 Task.Status 属性是 TaskStatus.Canceled,则前面的任务会取消。 此选项对多任务延续无效。
    OnlyOnFaulted 指定只有在延续任务前面的任务引发了未处理异常的情况下才应安排延续任务。 如果前面任务完成的 Task.Status 属性是 TaskStatus.Faulted,则前面的任务会引发未处理的异常。OnlyOnFaulted 选项可保证前面任务中的 Task.Exception 属性不是 null。 你可以使用该属性来捕获异常,并确定导致任务出错的异常。 如果你不访问 Exception 属性,则不会处理异常。 此外,如果尝试访问已取消或出错的任务的 Result 属性,则会引发一个新异常。此选项对多任务延续无效。
    OnlyOnRanToCompletion 指定只应在延续任务前面的任务已完成运行的情况下才安排延续任务。 如果前面任务完成的 Task.Status 属性是 TaskStatus.RanToCompletion,则前面的任务会运行直至完成。 此选项对多任务延续无效。
    PreferFairness 提示 TaskScheduler 按任务计划的顺序安排任务,因此较早安排的任务将更可能较早运行,而较晚安排运行的任务将更可能较晚运行。
    RunContinuationsAsynchronously 指定应异步运行延续任务。 此选项优先于 TaskContinuationOptions.ExecuteSynchronously。

    演示代码如下所示,使用ContinueWith()OnCompleted()办法结合了职务来运维,搭配区别的TaskCreationOptionsTaskContinuationOptions来兑现分裂的成效。

    static void Main(string[] args)
    {
        WriteLine($"主线程 线程 Id {CurrentThread.ManagedThreadId}");
    
        // 创建两个任务
        var firstTask = new Task<int>(() => TaskMethod("Frist Task",3));
        var secondTask = new Task<int>(()=> TaskMethod("Second Task",2));
    
        // 在默认的情况下 ContiueWith会在前面任务运行后再运行
        firstTask.ContinueWith(t => WriteLine($"第一次运行答案是 {t.Result}. 线程Id {CurrentThread.ManagedThreadId}. 是否为线程池线程: {CurrentThread.IsThreadPoolThread}"));
    
        // 启动任务
        firstTask.Start();
        secondTask.Start();
    
        Sleep(TimeSpan.FromSeconds(4));
    
        // 这里会紧接着 Second Task运行后运行, 但是由于添加了 OnlyOnRanToCompletion 和 ExecuteSynchronously 所以会由运行SecondTask的线程来 运行这个任务
        Task continuation = secondTask.ContinueWith(t => WriteLine($"第二次运行的答案是 {t.Result}. 线程Id {CurrentThread.ManagedThreadId}. 是否为线程池线程:{CurrentThread.IsThreadPoolThread}"),TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.ExecuteSynchronously);
    
        // OnCompleted 是一个事件  当contiuation运行完成后 执行OnCompleted Action事件
        continuation.GetAwaiter().OnCompleted(() => WriteLine($"后继任务完成. 线程Id {CurrentThread.ManagedThreadId}. 是否为线程池线程 {CurrentThread.IsThreadPoolThread}"));
    
        Sleep(TimeSpan.FromSeconds(2));
        WriteLine();
    
        firstTask = new Task<int>(() => 
        {
            // 使用了TaskCreationOptions.AttachedToParent 将这个Task和父Task关联, 当这个Task没有结束时  父Task 状态为 WaitingForChildrenToComplete
            var innerTask = Task.Factory.StartNew(() => TaskMethod("Second Task",5), TaskCreationOptions.AttachedToParent);
    
            innerTask.ContinueWith(t => TaskMethod("Thrid Task", 2), TaskContinuationOptions.AttachedToParent);
    
            return TaskMethod("First Task",2);
        });
    
        firstTask.Start();
    
        // 检查firstTask线程状态  根据上面的分析 首先是  Running -> WatingForChildrenToComplete -> RanToCompletion
        while (! firstTask.IsCompleted)
        {
            WriteLine(firstTask.Status);
    
            Sleep(TimeSpan.FromSeconds(0.5));
        }
    
        WriteLine(firstTask.Status);
    
        Console.ReadLine();
    }
    
    static int TaskMethod(string name, int seconds)
    {
        WriteLine($"任务 {name} 正在运行,线程池线程 Id {CurrentThread.ManagedThreadId},是否为线程池线程: {CurrentThread.IsThreadPoolThread}");
    
        Sleep(TimeSpan.FromSeconds(seconds));
    
        return 42 * seconds;
    }
    

    运行结果如下图所示,与预期结果一律。其中使用了task.Status来打印使时局维的情景,对于task.Status的动静具体意思如下表所示。

    成员名称 说明
    Canceled 该任务已通过对其自身的 CancellationToken 引发 OperationCanceledException 对取消进行了确认,此时该标记处于已发送信号状态;或者在该任务开始执行之前,已向该任务的 CancellationToken 发出了信号。 有关详细信息,请参阅任务取消
    Created 该任务已初始化,但尚未被计划。
    Faulted 由于未处理异常的原因而完成的任务。
    RanToCompletion 已成功完成执行的任务。
    Running 该任务正在运行,但尚未完成。
    WaitingForActivation 该任务正在等待 .NET Framework 基础结构在内部将其激活并进行计划。
    WaitingForChildrenToComplete 该任务已完成执行,正在隐式等待附加的子任务完成。
    WaitingToRun 该任务已被计划执行,但尚未开始执行。

    图片 10

    线程池相当于线程和用户之间的一个抽象层,向技术员隐藏了使用线程的细节,使得程序员专心管理程序逻辑,而不是各样线程难题。

    异步和等候


    如若经过 async 修饰符钦点某种情势为异步方法,则足以启用以下四个功效。

    • 标记的异步方法能够使用 await 来内定悬挂点。await 运算符公告编写翻译器异步方法唯有直到等待的异步进程一呵而就技术接二连三透过该点。 同临时间,控件重返至异步方法的调用方。 await 表达式中异步方法的挂起不能够使该格局退出,并且 finally 块不会运营。
    • 标记的异步方法自个儿能够因此调用它的办法等待

    异步方法一般包蕴 await 运算符的多少个或多少个相称项,但贫乏 await 表达式不会招致编写翻译器错误。 若是异步方法未接纳 await 运算符标志悬挂点,则该措施将用作合办方法施行,不管异步修饰符怎么样。编写翻译器将为此类措施公布二个警示。

    Async 、async、Await 和 await 都以左右文关键字。 有关越来越多音讯和演示,请参见以下核心:

    • async
    • await

    异步和等候


    例如经过 async 修饰符钦点某种情势为异步方法,则足以启用以下八个作用。

    • 标识的异步方法能够运用 await 来钦定悬挂点。await 运算符通告编写翻译器异步方法唯有直到等待的异步进度一挥而就技巧一而再透过该点。 同期,控件重临至异步方法的调用方。 await 表明式中异步方法的挂起不可能使该格局退出,并且 finally 块不会运转。
    • 标识的异步方法本人能够因而调用它的秘技等待

    异步方法一般包蕴 await 运算符的一个或七个相称项,但贫乏 await 表明式不会招致编写翻译器错误。 若是异步方法未采用 await 运算符标志悬挂点,则该措施将用香港作家联谊会见进行方法推行,不管异步修饰符如何。编写翻译器将为此类措施公布二个警示。

    Async 、async、Await 和 await 都以左右文关键字。 有关更加多消息和演示,请参见以下主旨:

    • async
    • await

    1.5 将APM格局转变为义务

    在后边的章节中,介绍了基于IAsyncResult接口实现了BeginXXXX/EndXXXX主意的就叫APM方式。APM情势极其古老,那么哪些将它调换为TAP格局呢?对于广大的两种APM形式异步职责,我们一般采纳使用Task.Factory.FromAsync()方法来完成将APM模式转换为TAP模式

    演示代码如下所示,相比较轻松不作过多介绍。

    static void Main(string[] args)
    {
        int threadId;
        AsynchronousTask d = Test;
        IncompatibleAsychronousTask e = Test;
    
        // 使用 Task.Factory.FromAsync方法 转换为Task
        WriteLine("Option 1");
        Task<string> task = Task<string>.Factory.FromAsync(d.BeginInvoke("异步任务线程", CallBack, "委托异步调用"), d.EndInvoke);
    
        task.ContinueWith(t => WriteLine($"回调函数执行完毕,现在运行续接函数!结果:{t.Result}"));
    
        while (!task.IsCompleted)
        {
            WriteLine(task.Status);
            Sleep(TimeSpan.FromSeconds(0.5));
        }
        WriteLine(task.Status);
        Sleep(TimeSpan.FromSeconds(1));
    
        WriteLine("----------------------------------------------");
        WriteLine();
    
        // 使用 Task.Factory.FromAsync重载方法 转换为Task
        WriteLine("Option 2");
    
        task = Task<string>.Factory.FromAsync(d.BeginInvoke,d.EndInvoke,"异步任务线程","委托异步调用");
    
        task.ContinueWith(t => WriteLine($"任务完成,现在运行续接函数!结果:{t.Result}"));
    
        while (!task.IsCompleted)
        {
            WriteLine(task.Status);
            Sleep(TimeSpan.FromSeconds(0.5));
        }
        WriteLine(task.Status);
        Sleep(TimeSpan.FromSeconds(1));
    
        WriteLine("----------------------------------------------");
        WriteLine();
    
        // 同样可以使用 FromAsync方法 将 BeginInvoke 转换为 IAsyncResult 最后转换为 Task
        WriteLine("Option 3");
    
        IAsyncResult ar = e.BeginInvoke(out threadId, CallBack, "委托异步调用");
        task = Task<string>.Factory.FromAsync(ar, _ => e.EndInvoke(out threadId, ar));
    
        task.ContinueWith(t => WriteLine($"任务完成,现在运行续接函数!结果:{t.Result},线程Id {threadId}"));
    
        while (!task.IsCompleted)
        {
            WriteLine(task.Status);
            Sleep(TimeSpan.FromSeconds(0.5));
        }
        WriteLine(task.Status);
    
        ReadLine();
    }
    
    delegate string AsynchronousTask(string threadName);
    delegate string IncompatibleAsychronousTask(out int threadId);
    
    static void CallBack(IAsyncResult ar)
    {
        WriteLine("开始运行回调函数...");
        WriteLine($"传递给回调函数的状态{ar.AsyncState}");
        WriteLine($"是否为线程池线程:{CurrentThread.IsThreadPoolThread}");
        WriteLine($"线程池工作线程Id:{CurrentThread.ManagedThreadId}");
    }
    
    static string Test(string threadName)
    {
        WriteLine("开始运行...");
        WriteLine($"是否为线程池线程:{CurrentThread.IsThreadPoolThread}");
        Sleep(TimeSpan.FromSeconds(2));
    
        CurrentThread.Name = threadName;
        return $"线程名:{CurrentThread.Name}";
    }
    
    static string Test(out int threadId)
    {
        WriteLine("开始运行...");
        WriteLine($"是否为线程池线程:{CurrentThread.IsThreadPoolThread}");
        Sleep(TimeSpan.FromSeconds(2));
    
        threadId = CurrentThread.ManagedThreadId;
        return $"线程池线程工作Id是:{threadId}";
    }
    

    运转结果如下图所示。

    图片 11

    只是使用线程池也很复杂。有五个难点存在:

    回到类型和参数


    .NET Framework 异步编制程序中异步方法一般重回 Task 或 Task<TResult>。 在异步方法中,await 运算符应用于通过调用另贰个异步方法再次来到的职分。

    万一措施包涵 Return (Visual Basic) 或钦定项目 TResult 的操作数的 return (C#) 语句,则将 Task<TResult> 钦点为回到类型。

    若是格局不含任何 return 语句或含有不回来操作数的 return 语句,则将 Task 用作再次来到类型。

    下边的示范演示怎么着注明并调用可回到 Task<TResult> 或 Task 的主意。

    // Signature specifies Task<;TResult>
    
    async Task<;int> TaskOfTResult_MethodAsync()
    
    {
    
        int hours;
    
        // . . .
    
        // Return statement specifies an integer result.
    
        return hours;
    
    }
    
     
    
    // Calls to TaskOfTResult_MethodAsync
    
    Task<;int> returnedTaskTResult = TaskOfTResult_MethodAsync();
    
    int intResult = await returnedTaskTResult;
    
    // or, in a single statement
    
    int intResult = await TaskOfTResult_MethodAsync();
    
    // Signature specifies Task
    
    async Task Task_MethodAsync()
    
    {
    
        // . . .
    
        // The method has no return statement.  
    
    }
    
     
    
    // Calls to Task_MethodAsync
    
    Task returnedTask = Task_MethodAsync();
    
    await returnedTask;
    
    // or, in a single statement
    
    await Task_MethodAsync();
    

    每一个重临的职责表示正在进展的职业。 职责可包裹有关异步进度情状的新闻,假若未得逞,则最终会卷入来自进程的末尾结果或进度引发的不胜。

    异步方法还是能够是 Sub 方法 (Visual Basic) 或具备 void 重临类型 (C#)。 该再次来到类型重要用来定义供给 void 再次回到类型的事件处理程序。 异步事件管理程序经常作为异步程序的发轫点。

    不可能等待为 Sub 程序或具有 void 重返类型的异步方法,并且无效的回来方法的调用方不能捕获该格局引发的任何特别。

    异步方法不大概注解 Visual Basic 中的 ByRef 参数或 C# 中的 ref 或 out 参数,但此办法可以调用具备此类参数的方法。

    至于越多音讯和演示,请参见异步再次来到类型(C# 和 Visual Basic)。 有关怎么样在异步方法中捕捉至极的越多音信,请参见 try-catch(C# 参考)或 Try...Catch...Finally 语句 (Visual Basic)。

    Windows 运营时编制程序中的异步 API 具备下列重返类型之一,它就像于职责:

    • IAsyncOperation,它对应于 Task<TResult>
    • IAsyncAction,它对应于 Task
    • IAsyncActionWithProgress
    • IAsyncOperationWithProgress

    回去类型和参数


    .NET Framework 异步编制程序中异步方法一般再次回到 Task 或 Task<TResult>。 在异步方法中,await 运算符应用于通过调用另七个异步方法重回的职分。

    一旦艺术包罗 Return (Visual Basic) 或钦赐项目 TResult 的操作数的 return 语句,则将 Task<TResult> 内定为回去类型。

    若是措施不含任何 return 语句或蕴涵不回去操作数的 return 语句,则将 Task 用作再次来到类型。

    下边包车型客车演示演示怎么样证明并调用可重回 Task<TResult> 或 Task 的章程。

    // Signature specifies Task<;TResult>
    
    async Task<;int> TaskOfTResult_MethodAsync()
    
    {
    
        int hours;
    
        // . . .
    
        // Return statement specifies an integer result.
    
        return hours;
    
    }
    
     
    
    // Calls to TaskOfTResult_MethodAsync
    
    Task<;int> returnedTaskTResult = TaskOfTResult_MethodAsync();
    
    int intResult = await returnedTaskTResult;
    
    // or, in a single statement
    
    int intResult = await TaskOfTResult_MethodAsync();
    
    // Signature specifies Task
    
    async Task Task_MethodAsync()
    
    {
    
        // . . .
    
        // The method has no return statement.  
    
    }
    
     
    
    // Calls to Task_MethodAsync
    
    Task returnedTask = Task_MethodAsync();
    
    await returnedTask;
    
    // or, in a single statement
    
    await Task_MethodAsync();
    

    各种再次来到的职务表示正在张开的做事。 任务可包裹有关异步进程景况的音讯,即使未中标,则最终会卷入来自进度的尾声结出或进程引发的非常。

    异步方法还是能够是 Sub 方法 (Visual Basic) 或富有 void 再次回到类型 。 该重回类型紧要用于定义供给 void 再次回到类型的事件管理程序。 异步事件管理程序平时作为异步程序的开端点。

    不知所可等待为 Sub 程序或具有 void 再次来到类型的异步方法,并且无效的回来方法的调用方不或然捕获该方法引发的任何非常。

    异步方法不可能注脚 Visual Basic 中的 ByRef 参数或 C# 中的 ref 或 out 参数,但此方式能够调用具有此类参数的方法。

    至于越多音信和示范,请参见异步再次回到类型(C# 和 Visual Basic)。 有关怎样在异步方法中捕捉极度的越来越多音信,请参见 try-catch或 Try...Catch...Finally 语句 (Visual Basic)。

    Windows 运维时编制程序中的异步 API 具有下列重回类型之一,它就好像于职务:

    • IAsyncOperation,它对应于 Task<TResult>
    • IAsyncAction,它对应于 Task
    • IAsyncActionWithProgress
    • IAsyncOperationWithProgress

    1.6 将EAP方式调换为天职

    在上几章中有关系,通过BackgroundWorker类经过事件的法子贯彻的异步,大家叫它EAP情势。那么哪些将EAP情势转变为天职吗?很简单,大家只供给通过TaskCompletionSource类,就能够将EAP情势转变为职分。

    演示代码如下所示。

    static void Main(string[] args)
    {
        var tcs = new TaskCompletionSource<int>();
    
        var worker = new BackgroundWorker();
        worker.DoWork  = (sender, eventArgs) =>
        {
            eventArgs.Result = TaskMethod("后台工作", 5);
        };
    
        // 通过此方法 将EAP模式转换为 任务
        worker.RunWorkerCompleted  = (sender, eventArgs) =>
        {
            if (eventArgs.Error != null)
            {
                tcs.SetException(eventArgs.Error);
            }
            else if (eventArgs.Cancelled)
            {
                tcs.SetCanceled();
            }
            else
            {
                tcs.SetResult((int)eventArgs.Result);
            }
        };
    
        worker.RunWorkerAsync();
    
        // 调用结果
        int result = tcs.Task.Result;
    
        WriteLine($"结果是:{result}");
    
        ReadLine();
    }
    
    static int TaskMethod(string name, int seconds)
    {
        WriteLine($"任务{name}运行在线程{CurrentThread.ManagedThreadId}上. 是否为线程池线程{CurrentThread.IsThreadPoolThread}");
    
        Sleep(TimeSpan.FromSeconds(seconds));
    
        return 42 * seconds;
    }
    

    运维结果如下图所示。

    图片 12

    ①获取线程池中的工作线程的结果比较难

    参谋资料


    • Microsoft Developer Network 基于任务的异步模式(TAP)
    • 选择 Async 和 Await 的异步编制程序
    • 异步程序中的调控流

     

    下载 Demo

    下载 Demo TPL 与 AMP 和 EAP 结合

    参谋资料


    • Microsoft Developer Network 基于任务的异步格局
    • 利用 Async 和 Await 的异步编制程序
    • 异步程序中的调控流

     

    下载 Demo

    下载 Demo TPL 与 AMP 和 EAP 结合

    1.7 实现撤废选项

    在TAP形式中,达成撤消选项和事先的异步格局同样,都是利用CancellationToken来达成,但是分歧的是Task构造函数允许传入三个CancellationToken,从而在职分实际运转在此以前打消它。

    身体力行代码如下所示。

    static void Main(string[] args)
    {
        var cts = new CancellationTokenSource();
        // new Task时  可以传入一个 CancellationToken对象  可以在线程创建时  变取消任务
        var longTask = new Task<int>(() => TaskMethod("Task 1", 10, cts.Token), cts.Token);
        WriteLine(longTask.Status);
        cts.Cancel();
        WriteLine(longTask.Status);
        WriteLine("第一个任务在运行前被取消.");
    
        // 同样的 可以通过CancellationToken对象 取消正在运行的任务
        cts = new CancellationTokenSource();
        longTask = new Task<int>(() => TaskMethod("Task 2", 10, cts.Token), cts.Token);
        longTask.Start();
    
        for (int i = 0; i < 5; i  )
        {
            Sleep(TimeSpan.FromSeconds(0.5));
            WriteLine(longTask.Status);
        }
        cts.Cancel();
        for (int i = 0; i < 5; i  )
        {
            Sleep(TimeSpan.FromSeconds(0.5));
            WriteLine(longTask.Status);
        }
    
        WriteLine($"这个任务已完成,结果为{longTask.Result}");
    
        ReadLine();
    }
    
    static int TaskMethod(string name, int seconds, CancellationToken token)
    {
        WriteLine($"任务运行在{CurrentThread.ManagedThreadId}上. 是否为线程池线程:{CurrentThread.IsThreadPoolThread}");
    
        for (int i = 0; i < seconds; i  )
        {
            Sleep(TimeSpan.FromSeconds(1));
            if (token.IsCancellationRequested)
            {
                return -1;
            }
        }
    
        return 42 * seconds;
    }
    

    运转结果如下图所示,这里须要专注的是,借使是在任务实施在此之前裁撤了任务,那么它的末梢状态是Canceled。要是是在施行进程中撤消义务,那么它的情况是RanCompletion

    图片 13

    ②达成线程池中央银行事线程施行的时序难题

    1.8 管理职责中的极度

    异步编制程序,使用职务并行库。在任务中,处理特别和别的异步格局处理特别类似,要是能在所爆发特别的线程中处理,那么毫不在任哪个地方方处理。不过对于有些不得预期的不得了,那么能够经过二种艺术来拍卖。

    能够经过访问task.Result质量来处理特别,因为访问那一个天性的Get主意会使前段时间线程等待直到该职务完毕,并将卓越传播给当下线程,那样就可以通过try catch语句块来捕获十分。此外利用task.GetAwaiter().GetResult()主意和第使用task.Result看似,同样能够捕获相当。要是是要捕获多少个任务中的相当错误,那么能够经过ContinueWith()措施来管理。

    切切实实哪些促成,演示代码如下所示。

    static void Main(string[] args)
    {
        Task<int> task;
        // 在主线程中调用 task.Result task中的异常信息会直接抛出到 主线程中
        try
        {
            task = Task.Run(() => TaskMethod("Task 1", 2));
            int result = task.Result;
            WriteLine($"结果为: {result}");
        }
        catch (Exception ex)
        {
            WriteLine($"异常被捕捉:{ex.Message}");
        }
        WriteLine("------------------------------------------------");
        WriteLine();
    
        // 同上 只是访问Result的方式不同
        try
        {
            task = Task.Run(() => TaskMethod("Task 2", 2));
            int result = task.GetAwaiter().GetResult();
            WriteLine($"结果为:{result}");
        }
        catch (Exception ex)
        {
            WriteLine($"异常被捕捉: {ex.Message}");
        }
        WriteLine("----------------------------------------------");
        WriteLine();
    
        var t1 = new Task<int>(() => TaskMethod("Task 3", 3));
        var t2 = new Task<int>(() => TaskMethod("Task 4", 4));
    
        var complexTask = Task.WhenAll(t1, t2);
        // 通过ContinueWith TaskContinuationOptions.OnlyOnFaulted的方式 如果task出现异常 那么才会执行该方法
        var exceptionHandler = complexTask.ContinueWith(t => {
            WriteLine($"异常被捕捉:{t.Exception.Message}");
            foreach (var ex in t.Exception.InnerExceptions)
            {
                WriteLine($"-------------------------- {ex.Message}");
            }
        },TaskContinuationOptions.OnlyOnFaulted);
    
        t1.Start();
        t2.Start();
    
        ReadLine();
    }
    
    static int TaskMethod(string name, int seconds)
    {
        WriteLine($"任务运行在{CurrentThread.ManagedThreadId}上. 是否为线程池线程:{CurrentThread.IsThreadPoolThread}");
    
        Sleep(TimeSpan.FromSeconds(seconds));
        // 人为抛出一个异常
        throw new Exception("Boom!");
        return 42 * seconds;
    }
    

    运营结果如下所示,需求注意的是,要是在ContinueWith()艺术中抓获五个职责发生的非常,那么它的特别类型是AggregateException,具体的特别消息包括在InnerExceptions在那之中,要注意和InnerException区分。

    图片 14

    综上,大家在第3章中提过的异步编制程序模型和依附事件的异步编制程序模型,那个格局使得获取结果更是便于,传播也更轻易,可是在展开五个异步操作结合的时候,还索要编写制定大批量的代码。对于首个难点.NET 4.0提议了二个新的关于异步操作的API。叫做任务并行库(Task Parallel Library 简称 TPL)。

    1.9 相互运行职责

    本节中最主要介绍了四个艺术的行使,多个是伺机组中全体任务都试行完成的Task.WhenAll()办法,另三个是一旦组中八个主意推行完结都推行的Task.WhenAny()方法。

    切切实进行使,如下演示代码所示。

    static void Main(string[] args)
    {
        // 第一种方式 通过Task.WhenAll 等待所有任务运行完成
        var firstTask = new Task<int>(() => TaskMethod("First Task", 3));
        var secondTask = new Task<int>(() => TaskMethod("Second Task", 2));
    
        // 当firstTask 和 secondTask 运行完成后 才执行 whenAllTask的ContinueWith
        var whenAllTask = Task.WhenAll(firstTask, secondTask);
        whenAllTask.ContinueWith(t => WriteLine($"第一个任务答案为{t.Result[0]},第二个任务答案为{t.Result[1]}"), TaskContinuationOptions.OnlyOnRanToCompletion);
    
        firstTask.Start();
        secondTask.Start();
    
        Sleep(TimeSpan.FromSeconds(4));
    
        // 使用WhenAny方法  只要列表中有一个任务完成 那么该方法就会取出那个完成的任务
        var tasks = new List<Task<int>>();
        for (int i = 0; i < 4; i  )
        {
            int counter = 1;
            var task = new Task<int>(() => TaskMethod($"Task {counter}",counter));
            tasks.Add(task);
            task.Start();
        }
    
        while (tasks.Count > 0)
        {
            var completedTask = Task.WhenAny(tasks).Result;
            tasks.Remove(completedTask);
            WriteLine($"一个任务已经完成,结果为 {completedTask.Result}");
        }
    
        ReadLine();
    }
    
    static int TaskMethod(string name, int seconds)
    {
        WriteLine($"任务运行在{CurrentThread.ManagedThreadId}上. 是否为线程池线程:{CurrentThread.IsThreadPoolThread}");
    
        Sleep(TimeSpan.FromSeconds(seconds));
        return 42 * seconds;
    }
    

    运行结果如下图所示。

    图片 15

     

    1.10 使用TaskScheduler配置职务实施

    Task中,负担职责调解是TaskScheduler指标,FCL提供了四个派生自TaskScheduler的类型:线程池职务调整器(Thread Pool Task Scheduler)一道上下文职责调解器(Synchronization Scheduler)。私下认可情状下全数应用程序都使用线程池职责调整器,然则在UI组件中,不使用线程池中的线程,幸免跨线程更新UI,要求采纳同步上下文职责调节器。能够由此施行TaskSchedulerFromCurrentSynchronizationContext()静态方法来赢得对多头上下文职责调整器的引用。

    演示程序如下所示,为了延时联合上下文任务调治器,大家此次利用WPF来成立项目。

    MainWindow.xaml 代码如下所示。

    <Window x:Class="Recipe9.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:Recipe9"
            mc:Ignorable="d"
            Title="MainWindow" Height="450" Width="800">
        <Grid>
            <TextBlock Name="ContentTextBlock" HorizontalAlignment="Left" Margin="44,134,0,0" VerticalAlignment="Top" Width="425" Height="40"/>
            <Button Content="Sync" HorizontalAlignment="Left" Margin="45,190,0,0" VerticalAlignment="Top" Width="75" Click="ButtonSync_Click"/>
            <Button Content="Async" HorizontalAlignment="Left" Margin="165,190,0,0" VerticalAlignment="Top" Width="75" Click="ButtonAsync_Click"/>
            <Button Content="Async OK" HorizontalAlignment="Left" Margin="285,190,0,0" VerticalAlignment="Top" Width="75" Click="ButtonAsyncOK_Click"/>
        </Grid>
    </Window>
    

    MainWindow.xaml.cs 代码如下所示。

    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    
        // 同步执行 计算密集任务 导致UI线程阻塞
        private void ButtonSync_Click(object sender, RoutedEventArgs e)
        {
            ContentTextBlock.Text = string.Empty;
    
            try
            {
                string result = TaskMethod().Result;
                ContentTextBlock.Text = result;
            }
            catch (Exception ex)
            {
                ContentTextBlock.Text = ex.InnerException.Message;
            }
        }
    
        // 异步的方式来执行 计算密集任务 UI线程不会阻塞 但是 不能跨线程更新UI 所以会有异常
        private void ButtonAsync_Click(object sender, RoutedEventArgs e)
        {
            ContentTextBlock.Text = string.Empty;
            Mouse.OverrideCursor = Cursors.Wait;
    
            Task<string> task = TaskMethod();
            task.ContinueWith(t => {
                ContentTextBlock.Text = t.Exception.InnerException.Message;
                Mouse.OverrideCursor = null;
            }, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.FromCurrentSynchronizationContext());
        }
    
        // 通过 异步 和 FromCurrentSynchronizationContext方法 创建了线程同步的上下文  没有跨线程更新UI 
        private void ButtonAsyncOK_Click(object sender, RoutedEventArgs e)
        {
            ContentTextBlock.Text = string.Empty;
            Mouse.OverrideCursor = Cursors.Wait;
            Task<string> task = TaskMethod(TaskScheduler.FromCurrentSynchronizationContext());
    
            task.ContinueWith(t => Mouse.OverrideCursor = null,
                CancellationToken.None,
                TaskContinuationOptions.None,
                TaskScheduler.FromCurrentSynchronizationContext());
        }
    
        Task<string> TaskMethod()
        {
            return TaskMethod(TaskScheduler.Default);
        }
    
        Task<string> TaskMethod(TaskScheduler scheduler)
        {
            Task delay = Task.Delay(TimeSpan.FromSeconds(5));
    
            return delay.ContinueWith(t =>
            {
                string str = $"任务运行在{CurrentThread.ManagedThreadId}上. 是否为线程池线程:{CurrentThread.IsThreadPoolThread}";
    
                Console.WriteLine(str);
    
                ContentTextBlock.Text = str;
                return str;
            }, scheduler);
        }
    }
    

    运作结果如下所示,从左至右依次单击按键,前七个开关将会引发那几个。
    图片 16

    现实音讯如下所示。

    图片 17

    TPL能够看成线程池之上的又七个抽象层,其对程序猿隐藏了与线程池交互的最底层代码,并提供了更便于的细粒度的API。

    参谋书籍

    正文首要参照了以下几本书,在此对那些小编表示由衷的感恩荷德,多谢你们为.Net的弘扬所做的进献!

    1. 《CLR via C#》
    2. 《C# in Depth Third Edition》
    3. 《Essential C# 6.0》
    4. 《Multithreading with C# Cookbook Second Edition》
    5. 《C#三十二线程编制程序实战》

    源码下载点击链接 演示源码下载

    TPL的为主概念是职分。三个职务代表了一个异步操作,该操作能够使用各类形式运营,能够应用或不选择独立线程运行。

    作者水平有限,若是不当招待各位议论指正!

    理所必然想趁待业时期的年华读完《Multithreading with C# Cookbook Second 艾德ition》这本书,并且享受做的有关笔记;但是出于小编近日专门的学业规划和肉体原因,恐怕近来都尚马时间来更新这么些连串,无法做到几天一更。请大家多多原谅!不过我一定会将以此类别全部翻新完毕的!感激大家的帮衬!

    二个职分可以有七种格局和任何职分组合起来。比方,能够而且举行多少个任务,等待全体任务成功,然后运维三个职责对此前全部的义务结果举行一些计量。TPL与在此之前的格局比较,当中多个关键优势是其有着用于组合职责的便利的API。

    拍卖任务中的万分结果也可能有二种格局。一个职分能够由三种职分组成,那个职务也能够有各自的子职责,所以有贰个AggregateException的概念。这种非常能够捕获底层职分之中的有所非常,并同意单独管理这几个极其。

    C#5.0中得以行使await和async关键词以平滑的,舒服的法子张开操作职务。

     

    4.2 创制职责

    创制任务有三种艺术:

    1.直接创设任务实例,通超过实际例方法Start方法来运营职分

    2.施用静态方法Task.Run和Task.Factory.StartNew来创设职分,两者都无需体现的调用start方法运维职责,区别在于前者是后世的一种火速情势,后者能够运用附加的选项。

    例:
    1     class Program
    2     {
    3         static void Main(string[] args)
    4         {
    5             //第一种直接创建任务实例,需要用start方法来启动任务
    6             var t1 = new Task(() => TaskMethod("Task 1"));
    7             var t2 = new Task(() => TaskMethod("Task 2"));
    8             t2.Start();
    9             t1.Start();
    10           //第二种通过Task.Factory.StartNew来创建任务
    11           //这里Run方法只是Task.Factory.StartNew的一个快捷方式,Task.Factory.StartNew可以添加附加选项
    12           Task.Run(() => TaskMethod("Task 3"));
    13           Task.Factory.StartNew(() => TaskMethod("Task 4"));
    14           //我们标记了该任务是长时间任务,结果该任务没有使用线程池,而是在单独的线程中运行
    15           Task.Factory.StartNew(() => TaskMethod("Task 5"), TaskCreationOptions.LongRunning);
    16           Thread.Sleep(TimeSpan.FromSeconds(1));
    17         }
    18 
    19         static void TaskMethod(string name)
    20         {
    21             Console.WriteLine(
    22                                 "Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
    23                                  name,
    24                                 Thread.CurrentThread.ManagedThreadId, 
    25                                 Thread.CurrentThread.IsThreadPoolThread);
    26         }
    27   }
    

    图片 18

    ※由于并未对任务的时序做拍卖,所以一再施行每二回都大概不雷同。

    ※Task5选取的是单独线程的办法来运维,不过依照运转该职分的当前的任务调整程序(task scheduler),运营格局大概会差别。

     

    4.3采取职分实施基本的操作

    最主要介绍怎么着从职分中拿走结果。

    1     class Program
    2     {
    3         static void Main(string[] args)
    4         {
    5              //启动主线程
    6              TaskMethod("Main Thread Task");
    7              //创建一个任务Task1,进行线程的同步
    8              Task<int> task = CreateTask("Task 1");
    9              task.Start();
    10             //阻塞主线程,直到线程执行完成
    11             int result = task.Result;
    12             Console.WriteLine("Result is: {0}", result);
    13 
    14             //创建Taks2,使用RunSynchronously()方法进行同步
    15             task = CreateTask("Task 2");
    16             task.RunSynchronously();
    17             result = task.Result;
    18             Console.WriteLine("Result is: {0}", result);
    19 
    20             //创建Task3,此时不进行主线程的阻塞
    21             task = CreateTask("Task 3");
    22             Console.WriteLine(task.Status);
    23             task.Start();
    24 
    25             //循环打印task的状态,直到任务完成
    26             while (!task.IsCompleted)
    27             {
    28                 Console.WriteLine(task.Status);
    29                 Thread.Sleep(TimeSpan.FromSeconds(0.5));
    30             } 
    31             
    32             Console.WriteLine(task.Status);
    33             result = task.Result;
    34             Console.WriteLine("Result is: {0}", result);
    35         }
    36 
    37         //创建一个新任务
    38         static Task<int> CreateTask(string name)
    39         {
    40             return new Task<int>(() => TaskMethod(name));
    41         }
    42 
    43         //任务需要处理的方法
    44         static int TaskMethod(string name)
    45         {
    46             Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
    47             name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
    48             Thread.Sleep(TimeSpan.FromSeconds(2));
    49             return 42;
    50         }
    51    }
    

    进行结果:

    图片 19

     

    4.4 组合职分

    此地我会学习到什么将职分拓展重组,以及老爹和儿子职责之间的实行。废话不说,有码

    实例1:
    
    1     class Program
    2     {
    3         static void Main(string[] args)
    4         {
    5             //打印主线程
    6             TaskMethod("Main Task", 1);
    7             //创建两个任务
    8             var firstTask = new Task<int>(() => TaskMethod("First Task", 3));
    9             var secondTask = new Task<int>(() => TaskMethod("Second Task", 2));
    10 
    11             //设置firstTask的后续操作
    12             firstTask.ContinueWith(
    13                 t => Console.WriteLine("The first answer is {0}. Thread id {1}, is thread pool thread: {2}",
    14                     t.Result, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread),
    15                 TaskContinuationOptions.OnlyOnRanToCompletion);
    16 
    17              //启动两个任务
    18             firstTask.Start();
    19             secondTask.Start();
    20             //延时4秒,足够两个任务完成的时间※↓这句是关键
    21             Thread.Sleep(TimeSpan.FromSeconds(4));
    22 
    23             //为secondTask设置一个后续操作,TaskContinuationOptions.ExecuteSynchronously尝试同步方式执行后续操作
    24             Task continuation = secondTask.ContinueWith(
    25                 t => Console.WriteLine("The second answer is {0}. Thread id {1}, is thread pool thread: {2}",
    26                     t.Result, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread),
    27                 TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.ExecuteSynchronously);
    28 
    29             //为之前的后续操作也定义一个后续操作,这里使用了C#5.0的方法GetAwaiter().OnCompleted()
    30             continuation.GetAwaiter().OnCompleted(
    31                 () => Console.WriteLine("Continuation Task Completed! Thread id {0}, is thread pool thread: {1}",
    32                     Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread));
    33 
    34             Thread.Sleep(TimeSpan.FromSeconds(2));
    35             Console.WriteLine();
    36 
    37             Thread.Sleep(TimeSpan.FromSeconds(10));
    38         }
    39 
    40         static int TaskMethod(string name, int seconds)
    41         {
    42             Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
    43                 name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
    44             Thread.Sleep(TimeSpan.FromSeconds(seconds));
    45             return 42 * seconds;
    46         }
    47  }
    

    图片 20

    那边我们来看secondTask的接轨操作未有使用到线程池,为何吧?

    分解:由地方的代码大家看到,使用了TaskContinuationOptions.ExecuteSynchronously尝试同步格局进行后续操作,借使继续操作时间十分长暂,使用方面包车型地铁艺术特别有成效的,因为放置在主线程举办运营要比放置在线程池中运作要快,那怎么会现出如此的情景吧,正是下边标志的延时期码的功劳,这段延时期码使得SecondTask后续操作正好获得了前头职务实施的结果。今后小编把  Thread.Sleep(TimeSpan.FromSeconds(4));注释掉再试一下,结果如下:

    图片 21

    认为如同客栈打饭,四人吃饭,A帮B打饭。

    首先种是:A打完饭后,开采B刚来,就间接把饭给了B,然后B直接吃了

    第二种是:A打饭的时候,B正好也来了,于是四人联合签字站队,A打完饭后再把饭给了B

     

    例2:演示了瞬间老爹和儿子职责之间的关联。

    1 class Program
    2     {
    3         static void Main(string[] args)
    4         {
    5              //创建一个父任务
    6              var firstTask = new Task<int>(() =>
    7             {
    8                 //创建一个子任务,使用TaskCreationOptions.AttachedToParent来标识
    9                 var innerTask = Task.Factory.StartNew(
    10                                         () => TaskMethod("Second Task", 5), 
    11                                         TaskCreationOptions.AttachedToParent);
    12               //创建一个子任务的后续操作,该后续操作也会影响父任务
    13                innerTask.ContinueWith(
    14                                         t => TaskMethod("Third Task", 2), 
    15                                         TaskContinuationOptions.AttachedToParent);
    16                 return TaskMethod("First Task", 2);
    17             });
    18 
    19             //启动任务
    20             firstTask.Start();
    21 
    22             //循环打印任务的状态
    23             while (!firstTask.IsCompleted)
    24             {
    25                 Console.WriteLine(firstTask.Status);
    26                 Thread.Sleep(TimeSpan.FromSeconds(0.5));
    27             }
    28             Console.WriteLine(firstTask.Status);
    29 
    30             Thread.Sleep(TimeSpan.FromSeconds(10));
    31         }
    32 
    33         static int TaskMethod(string name, int seconds)
    34         {
    35             Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
    36                 name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
    37             Thread.Sleep(TimeSpan.FromSeconds(seconds));
    38             return 42 * seconds;
    39         }
    

    图片 22

    下边结果呈现,父职务必须等待全体的子职责到位工夫成就,但是看不出来他们是共同照旧异步试行的。因为从First Task和Sencod Task它们之间的运转时序上也看不出来他们是老爸执行完了再奉行的子任务,所以小编觉着把父职责的岁月调长一点,那回自家让父职分实施10s

    修改:

       return TaskMethod("First Task", 2);  →   return TaskMethod("First Task", 10);

    结果如下

    图片 23

    那回显得的都以firstTask的Running状态,所以理应能一定父亲和儿子之间默许意况下也是异步实践的。因为父职责必供给等子职责全停止技艺不辱任务。

     

     

    本文由新葡亰496net发布于奥门新萄京娱乐场,转载请注明出处:异步编制程序,使用职务并行库

    关键词: