C#非同步編程

来源:http://www.cnblogs.com/ctddjyds/archive/2017/08/24/7196501.html
-Advertisement-
Play Games

IO操作的MDA(Direct memory access)模式:直接訪問記憶體,是一種不經過CPU而直接進行記憶體數據存儲的數據交換模式,幾乎可以不損耗CPU的資源; CLR所提供的非同步編程模型就是充分利用硬體的DMA功能來釋放CPU的壓力;使用線程池進行管理,非同步將工作移交給線程池中的某個工作線程來 ...


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

它是併發的一種形式,它採用 future 模式或回調(callback)機制,以避免產生不必要的線程。一個 future(或 promise)類型代表一些即將完成的操作。在 .NET 中,新版 future 類型有Task 和Task<TResult>。 

非同步編程模式------利用委托和線程池實現的模式

APM 非同步編程模型,Asynchronous Programming Model            C#1.0

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

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

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屬性值側重與提示信息,而非操作

訪問非同步操作的結果,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

 

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

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • case when語句,用於計算條件列表並返回多個可能結果表達式之一。CASE 具有兩種格式:1.when when_expression,是使用簡單 CASE 格式時所計算的表達式。Input_expression 是任何有效的SQL表達式。2.when Boolean_expression,使用... ...
  • 在SQL Server中使用Multiple Server Query Execution這個功能做資料庫維護或腳本發佈時非常方便,昨天由於磁碟空間原因,刪除清理了大量的軟體和組件,結果導致SSMS客戶端出了問題,重裝過後,使用Multiple Server Query Execution時,出現了 ...
  • 1創建文件repo文件 #vim /etc/yum.repos.d/mongodb-org-3.4.repo [mongodb-org-3.4] name=MongoDB Repository baseurl=https://repo.mongodb.org/yum/redhat/$releasev ...
  • 三層架構 常見架構: 開發中常見的23種設計模式: 創建型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。 結構型模式,共七種:適配器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。 行為型模式,共十一種:策略模式、模板方法模式、觀察者模式、迭代子模式、 ...
  • 1 private string GetMD5(string sDataIn) 2 { 3 MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider(); 4 byte[] bytValue, bytHash; 5 byt... ...
  • ServiceHub.DataWarehouseHost.exe記憶體泄漏問題的處理。 ...
  • 1 using System; 2 using System.Reflection; 3 4 namespace DynamicCall 5 { 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 Console.WriteLin... ...
  • 前不久,在工作中由於預設(xihuan)使用Async、Await關鍵字受到了很多質問,所以由此引發這篇博文“為什麼我們要用Async/Await關鍵字”,請聽下麵分解: Async/Await關鍵字 Visual Studio(.net framework 4.5)提供了非同步編程模型,相比之前實現 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...