從C#5.0說起:再次總結C#異步調用方法發展史


C#發展至今,已經從最初的1.0到了5.0版本,其進化史如下,參考了C# 5.0 IN A NUTSHEL:

讓我們來回顧一下各個版本都帶來了什么:

  1. 1.0版本 - 基本C#語法。
  2. 2.0版本 - 泛型的支持,CLR進行了升級,從根本上支持了運行時泛型。
  3. 3.0版本 - LINQ,添加了from / join等類SQL關鍵字,添加了擴展函數,添加了編譯期動態類型var關鍵字。
  4. 4.0版本 - dynamic關鍵字,CLR進行升級,加入DLR,開始對動態進行友好的支持。同時加入動態參數、參數默認值、泛型協變等特性。
  5. 5.0版本-新的異步模型,新增了async/await等關鍵字,簡化並行計算Parallel。

可以看出作為編程語言的C#已經非常強大,單隨着時代的發展,C#依然在不斷的前進。每一代的C#都會在語法的調整之,外帶來一個新特性。從2.0的泛型、3.0的LINQ、4.0的dynamic到5.0的Async異步,每個版本的C#都有一個主導的思想,而其他細節的改進和調整則是圍繞着這個主導思想給予支持。

下面我們來看下C#5.0及之前版本,異步調用方法的各種實現方法。

首先我們來看一個普通的同步方法,如下:

using System;
using System.Net;

namespace NoAsync
{
    class Program
    {
        static void Main(string[] args)
        {
            ShowUriContent("http://www.cnblogs.com/DebugLZQ");
        }

        static void ShowUriContent(string uri)
        {
            using (WebClient client = new WebClient())
            {
                string text = client.DownloadString(uri);
                Display(text);
            }
        }

        static void Display(string text)
        {
            Console.WriteLine(text.Length);
        }
    }
}

同步方法會造成線程的阻塞。

因此我們有了異步的方法,最早期的異步方法時Begin/End模式(其實現方法一共有四種,請參考DebugLZQ前面的博文:.NET異步編程總結----四種實現模式)。

我們用Begin/End推薦模式來封裝這個同步方法以實現異步調用,如下:

using System;
using System.Threading;
using System.Net;

namespace AsyBeginEndNoEncapsulation
{
    class Program
    {
        static void Main(string[] args)
        {
            ShowUriContent("http://www.cnblogs.com/DebugLZQ");//原同步方法
            ShowUriContentAsync("http://www.cnblogs.com/DebugLZQ");//封裝后的異步方法

            Thread.Sleep(5000);
        }
        //------進行異步封裝
        public delegate void ShowUriContentDelegate(string text);
        static void ShowUriContentAsync(string uri)
        {
            ShowUriContentDelegate showUriContentDelegate = ShowUriContent;
            showUriContentDelegate.BeginInvoke(uri, ShowUriContentCompleted, showUriContentDelegate);
        }

        static void ShowUriContentCompleted(IAsyncResult result)
        {
            (result.AsyncState as ShowUriContentDelegate).EndInvoke(result);
        }
        //------原同步方法
        static void ShowUriContent(string uri)
        {
            using (WebClient client = new WebClient())
            {
                string text = client.DownloadString(uri);
                Display(text);
            }
        }

        static void Display(string text)
        {
            Console.WriteLine(text.Length);
        }
    }
}

最原始的封裝就是這個樣子。

可以利用C#新特性,如Action/Function、匿名方法、Lambda表達式等,簡寫(合寫)如下:

using System;
//using System.Collections.Generic;
//using System.Linq;
//using System.Text;
using System.Threading;
using System.Net;

namespace AsyBeginEndNoEncapsulationSimply
{
    class Program
    {
        static void Main(string[] args)
        {
            ShowUriContent("http://www.cnblogs.com/DebugLZQ");//原同步方法
            ShowUriContentAsync("http://www.cnblogs.com/DebugLZQ");  //進行異步封裝
            ShowUriContentAsync1("http://www.cnblogs.com/DebugLZQ");//簡化1:Action簡化
            ShowUriContentAsync2("http://www.cnblogs.com/DebugLZQ");//簡化2:匿名方法簡化
            ShowUriContentAsync3("http://www.cnblogs.com/DebugLZQ");//簡化3:Lambda簡化
            

            Thread.Sleep(50000);
        }
        //------進行異步封裝
        public delegate void ShowUriContentDelegate(string text);
        static void ShowUriContentAsync(string uri)
        {
            ShowUriContentDelegate showUriContentDelegate = ShowUriContent;
            showUriContentDelegate.BeginInvoke(uri, ShowUriContentCompleted, showUriContentDelegate);
        }

        static void ShowUriContentCompleted(IAsyncResult result)
        {
            (result.AsyncState as ShowUriContentDelegate).EndInvoke(result);
        }
        //------進行異步封裝--簡化1:Action簡化
        static void ShowUriContentAsync1(string uri)
        {
            Action<string> showUriContentDelegate = ShowUriContent;
            showUriContentDelegate.BeginInvoke(uri, ShowUriContentCompleted1, showUriContentDelegate);
        }

        static void ShowUriContentCompleted1(IAsyncResult result)
        {
            (result.AsyncState as Action<string>).EndInvoke(result);
        }
        //------簡化2:匿名方法簡化
        static void ShowUriContentAsync2(string uri)
        {
            Action<string> showUriContentDelegate = delegate(string uri_)
            {
                using (WebClient client = new WebClient())
                {
                    string text = client.DownloadString(uri_);
                    Display(text);
                }
            };
            showUriContentDelegate.BeginInvoke(uri, delegate(IAsyncResult result) { (result.AsyncState as Action<string>).EndInvoke(result); }, showUriContentDelegate);
        }
        //------簡化3:Lambda簡化
        static void ShowUriContentAsync3(string uri)
        {
            Action<string> showUriContentDelegate = ( uri_)=>
            {
                using (WebClient client = new WebClient())
                {
                    string text = client.DownloadString(uri_);
                    Display(text);
                }
            };
            showUriContentDelegate.BeginInvoke(uri, (result) => { (result.AsyncState as Action<string>).EndInvoke(result); }, showUriContentDelegate);
        }       
       
        //---------------------原同步方法
        static void ShowUriContent(string uri)
        {
            using (WebClient client = new WebClient())
            {
                string text = client.DownloadString(uri);
                Display(text);
            }
        }

        static void Display(string text)
        {
            Console.WriteLine(text.Length);
        }
    }
}

以上是我們最原始的實現方法,及利用新特性的各種變種寫法。

但是WebClient作為WebRequest的高層封裝,.NET已經幫我們把這個異步模式給封裝了(也就是說有些同步方法不需要我們自己進行封裝了)。

因此我們也可以如下:

using System;
using System.Threading;
using System.Net;

namespace AsyncBeginEndEncapsulation
{
    class Program
    {
        static void Main(string[] args)
        {
            ShowUriContent("http://www.cnblogs.com/DebugLZQ");
            ShowUriContent2("http://www.cnblogs.com/DebugLZQ");

            Console.WriteLine("Main thread continue...");
            Thread.Sleep(5000);
        }

        static void ShowUriContent(string uri)
        {
            using (WebClient client = new WebClient())
            {
                client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(Display);
                client.DownloadStringAsync(new Uri(uri));
            }
        }

        static void Display(object sender, DownloadStringCompletedEventArgs e)
        {
            Console.WriteLine(e.Result.Length);
        }

        //-------簡化的寫法
        static void ShowUriContent2(string uri)
        {
            using (WebClient client = new WebClient())
            {
                client.DownloadStringCompleted += (s, e) => { Console.WriteLine(e.Result.Length); };
                client.DownloadStringAsync(new Uri(uri));
            }
        }
    }
}

從上面.NET對Begin/End模式的主動封裝可以看出,其目的是為了簡化異步方法的調用,最終的目的是讓異步方法調用像我們最熟悉的同步方法調用那么簡單。
C#5.0引入了兩個關鍵字async、await以提供一種更為簡潔的異步方法調用模式。

我們實現如下(控制台入口點Main方法無法標記為async,因此我們用Winform程序演示):

using System;
using System.Windows.Forms;
using System.Net;

namespace AsyncAwaitWinForm
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private async void button1_Click(object sender, EventArgs e)
        {
            int length = await ShowUriContentAsyncAwait("http://www.cnblogs.com/DebugLZQ");
            textBox1.Text = length.ToString();
        }

         //
        async Task<int> ShowUriContentAsyncAwait(string uri)
        {
            using (WebClient client = new WebClient())
            {
                string text = client.DownloadString(uri);
                return text.Length;
            }
        }     
    }
}

需要說明的是async、await需要:Visual Studio 2010 + SP1+Visual Studio Async CTP,或是Visual Studio 2012.

 

Update: 關於 Parallel---Task,請參考DebugLZQ后續博文:Task and Parallel

 

update:關於Async await詳細,請參考DebugLZQ后續博文:

async wait

 

以上所有示例程序均由DebugLZQ動手編寫,可以正常運行,其結果顯而易見,因此沒有附上運行截圖。

科技不斷向前發展,期待以后版本的C#6,7,8,X....帶來的更多精彩~

把自己的理解分享給大家,共同交流進步,認識不斷提升~

 

Update: Read more

Difference between Delegate.BeginInvoke and Thread.Start

Differences in the different ways to make concurrent programs


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM