C#異步編程

 IO操做的MDA(Direct memory access)模式:直接訪問內存,是一種不通過CPU而直接進行內存數據存儲的數據交換模式,幾乎能夠不損耗CPU的資源;
 CLR所提供的異步編程模型就是充分利用硬件的DMA功能來釋放CPU的壓力;使用線程池進行管理,異步將工做移交給線程池中的某個工做線程來完成,直到異步完成,異步纔會經過回調的方式通知線程池,讓CLR響應異步完畢;web

它是併發編程的一種形式,採用 future 模式或回調(callback)機制,以免產生沒必要要的線程。一個 future(或 promise)類型表明一些即將完成的操做。新版 future 類型有Task 和Task<TResult>。 老式異步編程 API 中,採用回調或事件(event) ,而不是future。算法

異步編程的核心理念是異步操做:啓動了的操做將會在一段時間後完成。這個操做正在執行時,不會阻塞原來的線程。啓動了這個操做的線程,能夠繼續執行其餘任務。當操做完成時,會通知它的 future,或者調用回調函數,以便讓程序知道操做已經結束。編程

異步編程模式------利用委託和線程池實現的模式

APM 異步編程模型,Asynchronous Programming Model            C#1.0windows

EAP 基於事件的異步編程模式,Event-based Asynchronous Pattern  C#2.0設計模式

TAP 基於任務的異步編程模式,Task-based Asynchronous Pattern    C#4.0promise

Async\await簡化異步編程;任務並行庫,Task Parallel Library     C#5多線程

APM

         使用IAsyncResult設計模式的異步操做是經過名爲 BeginXXX 和 EndXXX 的兩個方法來實現,這兩個方法分別指開始和結束異步操做。該模式容許用更少的CPU資源(線程)去作更多的操做,.NET Framework不少類也實現了該模式,同時咱們也能夠自定義類來實現該模式(也就是在自定義的類中實現返回類型爲IAsyncResult接口的BeginXXX方法和接受IAsyncResult兼容類型做爲惟一參數的EndXXX方法),另外委託類型也定義了BeginInvokeEndInvoke方法。例如,FileStream類提供BeginRead和EndRead方法來從文件異步讀取字節。這兩個方法實現了 Read 方法的異步版本。併發

調用 BeginXXX 後,應用程序能夠繼續在調用線程上執行指令,同時異步操做在另外一個線程上執行(若是有返回值還應調用 EndXXX結束異步操做,並向該方法傳遞BeginXXX 方法返回的IAsyncResult對象,獲取操做的返回值)。框架

 

CompletedSynchronously屬性值側重與提示信息,而非操做asp.net

訪問異步操做的結果,APM提供了四種方式:

1.在調用BeginXXX方法的線程上調用EndXXX方法來獲得異步操做的結果;可是這種方式會阻塞調用線程,在知道操做完成以後調用線程才能繼續運行。

2.循環查詢IAsyncResult的IsComplete屬性,操做完成後再調用EndXXX方法來得到操做返回的結果。

3.IAsyncResult的AsyncWaitHandle屬性實現更加靈活的等待邏輯,調用該屬性WaitOne()方法來使一個線程阻塞並等待操做完成;再調用EndXXX方法來得到操做的結果。WaitHandle.WaitOne()能夠指定最長的等待時間,如超時返回false;

4. 在調用BeginXXX方法時提供AsyncCallback委託的實例做爲參數,在異步操做完成後委託會自動調用(AsyncCallback對象)指定的方法。(首選方式)AsyncCallback委託僅可以調用符合特定模式的方法(只有一個參數IAsyncResult,且沒有返回值);

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裏面,之因此可以捕獲到,是由於發生異常的代碼與調用的代碼位於同一個線程。當調用一個異步方法發生異常時,CLR會捕獲而且在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界面控件的機制(將回調方法投遞到建立該控件的線程中執行)。

 

Control類的 Invoke,BeginInvoke 內部實現以下:

a) Invoke(同步調用)先判斷控件建立線程與當前線程是否相同,相同則直接調用委託方法;不然使用Win32API的PostMessage異步執行,可是Invoke內部會調用IAsyncResult.AsyncWaitHandle等待執行完成。

b) BeginInvoke(異步調用)使用Win32API的PostMessage 異步執行,而且返回 IAsyncResult 對象。

使用方式:回調方法中對控件檢測InvokeRequired值,if true,在該回調中封送一次委託,調用控件的Invoke/ BeginInvoke方法;

 

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分離來說的話是比較好;

EAP

EAP是爲了更便於處理UI的更新推出的模式,主要優勢:它同Visual Studio UI設計器進行了很好的集成,可將大多數實現了EAP的類拖放到設計平面(design surface)上,雙擊控件對應的XXXCompleted事件名,會自動生成事件的回調方法,並將方法同事件自身聯繫起來。EAP保證事件在應用程序的GUI線程上引起,容許事件回調方法中的代碼更新UI控件;
EAP另外一重要功能:支持EAP的類自動將應用程序模型映射到它的線程處理模型;EAP類在內部使用SynchronizationContext類。有的EAP類提供了取消、進度報告功能。

   FCL中只有17個類型實現了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登記的方法的DoWorkEventArgs參數對象的Argument屬性傳值,只能在登記的方法中(如e.Argument)獲取,Result屬性必須設置成計算限制的操做但願返回的值)時引起;

ProgressChanged:向這個事件登記的方法應該包含使用進度信息來更新UI的代碼。這個事件老是在GUI線程上引起。DoWork登記的方法必須按期調用BackgroundWorker的ReportProgress方法來引起ProgressChanged事件;

RunWorkerCompleted:向這個事件登記的方法應該包含使用計算限制操做的結果對UI進行更新的代碼。這個事件老是在GUI線程上引起。Result獲取表示異步操做的結果;

公共屬性:CancellationPending(標識是否已請求取消後臺操做)、IsBusy(標識是否正在運行異步操做)、WorkReportsProgress(獲取/設置是否報告進度更新)、WorkerSupportsCancellation(獲取/設置是否支持異步取消)

公共方法:CancelAsync(請求取消掛起的後臺操做)、ReportProgress、RunWorkerAsync

異常

異常不會拋出。在XXXCompleted事件處理方法中,必須查詢AsyncCompletedEventArgs的Exception屬性,看它是否是null。若是不是null,就必須使用if語句判斷Exception派生對象的類型,而不是使用catch塊。

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類進行包裝;

當構造一個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被垃圾回收時,若是存在一個沒有注意到的異常,CLR的終結器線程會引起這個事件。

可在事件回調函數中調用UnobservedTaskExceptionEventArgs對象的SetObserved() 方法來指出已經處理好了異常,從而阻止CLR終止線程。然而並不推薦這麼作,寧願終止進程也不要帶着已經損壞的狀態繼續運行。

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修飾符。

相關文章
相關標籤/搜索