C#基礎——談談.NET異步編程的演變史

前言web

C#5.0最重要的改進,就是提供了更強大的異步編程。C#5.0僅增長兩個新的關鍵字:async和await。編程

使用異步編程,方法調用是在後臺運行(一般在線程或任務的幫助下),而且不會阻塞調用線程。多線程

本文將介紹3種不一樣模式的異步編程:異步模式,基於事件的異步模式和新增長的基於任務的異步編程模式(TAP)。TAP是利用async和await關鍵字來實現的。經過這裏的比較,將認識到新的增長的基於任務的異步模式的真正優點。異步

假設情景:咱們須要進行一個耗時操做(這裏使用webclient對象下載百度首頁代碼),接下來經過同步以及上面談到的3種異步模式實現。async

同步調用 異步編程

建立一個控制檯應用程序:url

 
static void Main(string[] args)
        {
            WebClient client = new WebClient();
            client.Encoding = Encoding.UTF8;
            string resp = client.DownloadString("http://www.baidu.com");
            Console.WriteLine(resp);
            //todo:其餘操做
            Console.ReadKey();
        }

運行上述代碼,在執行DownloadString的時候,主線程會被阻塞,若是有用戶界面,那麼用戶體驗確定很差,而DownloadString這個方法的執行速度取決於網速等緣由,因此在這種狀況下,使用異步調用就很是有必要了。spa

異步模式線程

實現異步模式定義BeginXXX方法和EndXXX方法。例如,若是有一個同步方法DownloadString,異步方法將轉化成2個方法BeginDownloadString和EndDownloadString。BeginXXX異步方法接受同步方法的全部輸入參數以及一個AsyncCallback參數(一個委託,在異步方法執行完調用),返回一個IAsyncResult對象,用於驗證調用是否完成,EndXXX異步方法使用同步方法的全部輸出參數,並以同步方法的返回類型來返回結果(簡單來講就是獲取異步執行方法的返回值,若是在異步執行的方法還沒結束,則阻塞主線程直到異步方法執行結束)。code

WebClient並無異步模式的實現,可是能夠用HttpWebRequest類來代替(該類有BeginGetResponse和EndGetResponse):

 
static void Main(string[] args)
        {
            HttpWebRequest req = (HttpWebRequest)WebRequest.Create("http://www.baidu.com");
            req.BeginGetResponse(ar =>
            {
                HttpWebResponse resp = (HttpWebResponse)req.EndGetResponse(ar);

                Console.WriteLine(resp.ContentType );
                //todo:其餘操做
            }, null);
            Console.WriteLine("這是主線程");
            
            Console.ReadKey();
        }

若是非要用WebClient來實現異步模式,則須要用委託(只給代碼很少作介紹了):

 
static void Main(string[] args)
        {
           //定義委託實例
            Func<string, string> downloadString = url =>
            {
                WebClient client = new WebClient();
                client.Encoding = Encoding.UTF8;
              return client.DownloadString(url);
              
            };
            //異步執行委託
            downloadString.BeginInvoke("http://www.baidu.com", ar =>
            {
                string resp = downloadString.EndInvoke(ar);
                Console.WriteLine(resp);
            },null);
            Console.WriteLine("這是主線程");
            
            Console.ReadKey();
        }

異步模式的優點是使用委託就能實現異步編程。不用改變程序的行爲。也不會阻塞界面的操做。可是,使用異步模式的過程是很是複雜的(尤爲在winform或wpf中涉及更新界面操做時)。幸運的是,.NET2.0推出了基於事件的異步模式,能很輕鬆更新界面。

基於事件的異步模式

基於事件的異步模式定義了一個帶有「Async」後綴的方法。例如:對於同步方法DownloadString,WebClient類提供一個異步變體方法DownloadStringAsync。異步方法完成後,不是定義被調用的委託,而是定義事件。當異步方法DownloadStringAsync完成後,會直接調用DownloadStringCompleted事件,實現方式和上面差很少。但這時能夠直接訪問界面UI元素。

在這裏咱們換一個winform項目(界面只包含一個label和一個button),點擊button更新label值:

 
private void button1_Click(object sender, EventArgs e)
        {
           
            WebClient client = new WebClient();
            client.Encoding = Encoding.UTF8;
            client.DownloadStringCompleted += (sender1, e1) =>
            {
                string resp = e1.Result;
                label1.Text = resp.Substring(10);
            };
            client.DownloadStringAsync(new Uri("http://www.baidu.com"));
              
        }

基於事件的異步模式的優點在於易於使用。可是,若是在自定義類中實現這個模式,就沒那麼簡單了。方法仍是有的,可使用BackgroundWorker類來實現異步調用同步方法。BackgroundWorker類實現了基於事件的異步模式。還有,這種模式與同步方法調用相比,順序顛倒了。調用異步方法以前,須要定義這個方法完成時發生什麼。所以,下面進入異步編程的新世界,利用async和await關鍵字。

基於任務的異步模式(推薦使用)

此次先看代碼:

 
private async  void button1_Click(object sender, EventArgs e)
        {
           
            WebClient client = new WebClient();
            client.Encoding = Encoding.UTF8;

          string resp=await client.DownloadStringTaskAsync("http://www.baidu.com");
            label1.Text = resp.Substring(10);

        }

在.NET4.5中,更新了WebClient類,提供了基於任務的異步模式。該模式定義了一個帶有「Async」後綴的方法,並返回一個Task類型。因爲WebClient類已經提供了一個帶有「Async」後綴的方法,所以新方法名爲DownloadStringTaskAsync。

DownloadStringTaskAsync方法聲明返回Task<string>類型。可是,不須要爲DownloadStringTaskAsync方法返回的結果聲明一個Task<string>類型的變量。只要聲明一個string類型的變量,而且使用await關鍵字。await關鍵字不會阻塞完成其餘任務的線程。當DownloadStringTaskAsync方法完成其後臺處理後,UI線程會繼續並從後臺線程中得到結果,賦值給resp。同時,在await關鍵字的下一行代碼會繼續執行。

如今,代碼簡單多了。沒有阻塞,也不須要手工切回UI線程,這些都是自動實現的。代碼順序也和慣用的同步編程基本上同樣了。

轉換異步模式

並不是.NET FrameWork的全部類在.NET 4.5中都引入了新的異步方法,有些類仍是隻提供了BeginXXX和EndXXX方法的異步模式,那怎麼辦?

咱們以上面用到的HttpWebResponse爲例(假設它沒有新的異步方法):Task類型的泛型參數Task<WebResponse>,定義了調用方法的返回值類型,FromAsync方法的前兩個參數是委託類型,傳入對應的BeginXXX和EndXXX,因爲咱們沒有輸入參數,於是也沒有輸入參數的對象狀態,全部是null,代碼以下:

 
private async   void button1_Click(object sender, EventArgs e)
        {
            HttpWebRequest req = (HttpWebRequest)WebRequest.Create("http://www.baidu.com");

            HttpWebResponse resp = (HttpWebResponse)await Task<WebResponse>.Factory.FromAsync(req.BeginGetResponse, req.EndGetResponse, null);

        }
 

 

以前一直對這塊比較模糊,今天學了總結下,關於異步編程以及多線程還有好多東西要學,也很複雜,本文也就簡單介紹到這了

相關文章
相關標籤/搜索