日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

【转】.Net中的异步编程总结

發布時間:2023/12/10 asp.net 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【转】.Net中的异步编程总结 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一直以來很想梳理下我在開發過程中使用異步編程的心得和體會,但是由于我是APM異步編程模式的死忠,當TAP模式和TPL模式出現的時候我并未真正的去接納這兩種模式,所以導致我一直沒有花太多心思去整理這兩部分異步編程模型。今天在CodeProject上面偶然間看到一篇關于異步編程的文章,概括總結的非常好,省去了我自己寫的麻煩,索性翻譯過來,以饗各位。

??? ??在阻塞和并行編程過程中,異步和多線程是非常重要的特性。異步編程可以涉及到多線程,也可以不用涉及。如果我們能夠把二者放到一起來講解的話,我們會理解的更快一些。所以今天在這里,我主要想講解的內容是:

  • 異步編程
  • 是否需要多線程
  • TAP編程模型
  • 并行編程
  • 首先來說下異步編程

    所謂的異步編程就是指 當前的操作獨立于主操作流程。在C#編程中,我們總是從進入Main方法開始 ,Main方法返回結束。在開始和結束之間的所有操作,都會挨個的執行,下一個操作必須等到上一個操作完畢才開始執行,我們看看如下代碼:

    static void Main(string[] args){DoTaskOne();DoTaskTwo();}

    “DoTaskOne”方法必須在“DoTaskTwo”方法之前執行。也就是發生了阻塞現象。

    但是在異步編程中,方法的調用并不會阻塞主線程。當方法被調用后,主線程仍會繼續執行其他的任務。這種情形,一般都是使用Thread或者是Task來實現的。

    在上面的場景中,如果我們讓方法“DoTaskOne”異步執行,那么主線程將會立即返回,然后執行“DoTaskTwo”方法。

    在.NET中,我們可以利用Thread類或者異步模式來創建我們自己的線程來實現異步編程。在.NET中有三種不同的異步模型:

    1.APM模型

    2.EAP模型

    但是,遺憾的是,上面的這兩種模型都不是微軟所推薦的,所以我們將不對其做具體的討論。如果你對這兩種模型感興趣,請移步:

    https://msdn.microsoft.com/en-us/library/ms228963(v=vs.110).aspx

    https://msdn.microsoft.com/en-us/library/ms228969(v=vs.110).aspx

    3.TAP模型:這是微軟所推薦的,我們稍后將會詳細的進行講解。

    我們是否需要啟用多線程

    如果我們在.NET 4.5版本中使用異步編程模型,絕大部分情況下我們無需手動創建線程。因為編譯器已經為我們做了足夠多的工作了。

    創建一個新線程不僅耗費資源而且花費時間,除非我們真的需要去控制一個多線程,否則是不需要去創建的。因為TAP模型和TPL模型已經為異步編程和并行編程提供了絕好的支持。TAP模型和TPL模型使用Task來進行操作,而Task類使用線程池中的線程來完成操作的。

    Task可以在如下場景中運行:

  • 當前線程
  • 新線程
  • 線程池中的線程
  • 沒有線程
  • 在我們使用Task過程中,我們無需擔心線程的創建或者是使用,因為.NET framework已經為我們處理好了各種細節。但是如果我們需要控制線程的話,比如說以下的場景:

  • 為線程設置名稱
  • 為線程設置優先級
  • 設置線程為后臺線程
  • 那么我們不得不使用Thread類來控制。

    async和await關鍵字

    .NET framework引入了兩個新的關鍵字“async”和“await”來執行異步編程。當在方法上使用await關鍵字的時候,我們需要同時使用async關鍵字。await關鍵字是在調用異步方法之前被使用的。它主要是使方法的后續執行編程異步的,例如:

    private asyncstatic void CallerWithAsync()// async modifier is used {// await is used before a method call. It suspends //execution of CallerWithAsync() method and control returs to the calling thread that can//perform other task.string result = await GetSomethingAsync();// this line would not be executed before GetSomethingAsync() //method completesConsole.WriteLine(result); }

    在這里,async關鍵字只能夠被那些返回Task或者是void的方法所使用。它不能被用于Main入口方法上。

    我們不能夠在所有的方法上使用await關鍵字,因為一旦方法上有了await關鍵字,那么我們的返回類型就變成了“awaitable “類型。下面的幾種類型是” awaitable “的:

  • Task
  • Task<T>
  • TAP模型

    首先,我們需要一個能夠返回Task或者Task<T>的異步方法。我們可以通過如下方式來創建Task:

  • Task.Factory.StartNew方法:在.net 4.5版本之前(.net 4)中,這是默認的創建和組織task的方式。
  • Task.Run或者Task.Run<T>方法:從.net 4.5版本開始,這個方法被引進來了。這個方法足以能夠應付大多數的場景。
  • Task.FromResult方法:如果方法已經執行完畢并返回結果,我們可以使用這個方法來創建一個task。
  • Task.Factory.StartNew還有一些高級應用場景,請移步:http://blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx

    下面的鏈接則展示了創建Task的幾種方式:?
    http://dotnetcodr.com/2014/01/01/5-ways-to-start-a-task-in-net-c/

    ?

    Task的創建和等待

    我們接下來將會利用Task.Run<T>方法來創建我們自己的Task。它會將某個特殊的方法放入位于ThreadPool的執行隊列中,然后會為這些方法返回一個task句柄。下面的步驟將會為你展示我們如何為一個同步方法創建異步的Task的:

  • 我們有一個同步的方法,需要耗費一些時間來執行完畢:
  • static string Greeting(string name){Thread.Sleep(3000);return string.Format("Hello, {0}", name);}
  • 為了讓此方法一步執行,我們需要對其進行異步方法的包裝。這個異步方法我們暫定為“GreetingAsync“。
  • ?

    ?

    static Task<string> GreetingAsync(string name) {return Task.Run<string>(() =>{return Greeting(name);}); }

    ?

    ?

    現在我們可以使用await關鍵字來調用GreetingAsync方法

    ?

    ?

    private asyncstatic void CallWithAsync(){//some other tasksstring result = awaitGreetingAsync("Bulbul");//We can add multiple ‘await’ in same ‘async’ method//string result1 = await GreetingAsync(“Ahmed”);//string result2 = await GreetingAsync(“Every Body”);Console.WriteLine(result);}

    ?

    ?

    當“CallWithAsync”方法被調用的時候,它首先像正常的同步方法被調用,直到遇到await關鍵字。當它遇到await關鍵字的時候,它會暫定方法的執行,然后等待“GreetingAsync(“Bulbul”)”方法執行完畢。在此期間,這個控制流程會返回到“CallWithAsync”方法上,然后調用者就可以做其他的任務了。

    當“GreetingAsync(“Bulbul”)”方法執行完畢以后,“CallWithAsync”方法就會喚醒在await關鍵字后面的其他的方法,所以在這里它會繼續執行“Console.WriteLine(result)”方法。

  • 任務的繼續執行: “ContinueWith”方法則表明任務的持續執行。
  • private static void CallWithContinuationTask(){Task<string> t1 = GreetingAsync("Bulbul");t1.ContinueWith(t =>{string result = t.Result;Console.WriteLine(result);});}

    當我們使用ContinueWith方法的時候,我們無需使用await關鍵字。因為編譯器會自動的將await關鍵字放到正確的位置上。

    等待多個異步方法

    讓我們先看下面的代碼:

    private asyncstatic void CallWithAsync(){string result = awaitGreetingAsync("Bulbul");string result1 = awaitGreetingAsync("Ahmed");Console.WriteLine(result);Console.WriteLine(result1);}

    在這里,我們在順序的等待兩個方法被執行。GreetingAsync("Ahmed")方法將會在GreetingAsync("Bulbul")執行后,再執行。但是,如果result和result1彼此不是獨立的,那么await關鍵字這樣用是不合適的。

    在上面的場景中,我們其實是無需使用await關鍵字的。所以方法可以被改成如下的樣子:

    private async static void MultipleAsyncMethodsWithCombinators(){Task<string> t1 = GreetingAsync("Bulbul");Task<string> t2 = GreetingAsync("Ahmed");await Task.WhenAll(t1, t2);Console.WriteLine("Finished both methods.\n " +"Result 1: {0}\n Result 2: {1}", t1.Result, t2.Result);}

    在這里我們用了Task.WhenAll方法,它主要是等待所有的Task都完成工作后再觸發。Task類還有另一個方法:WhenAny,它主要是等待任何一個Task完成就會觸發。

    異常處理

    當進行錯誤處理的時候,我們不得不將await關鍵字放到try代碼塊中。

    private asyncstatic void CallWithAsync() {try{string result = awaitGreetingAsync("Bulbul");}catch (Exception ex){Console.WriteLine(&ldquo;handled {0}&rdquo;, ex.Message);} }

    但是,如果我們有多個await關鍵字存在于try代碼快中,那么只有第一個錯誤被處理,因為第二個await是無法被執行的。如果我們想要所有的方法都被執行,甚至當其中一個拋出異常的時候,我們不能使用await關鍵字來調用他們,我們需要使用Task.WhenAll方法來等待所有的task執行。

    private asyncstatic void CallWithAsync(){try{Task<string> t1 = GreetingAsync("Bulbul");Task<string> t2 = GreetingAsync("Ahmed");await Task.WhenAll(t1, t2);}catch (Exception ex){Console.WriteLine(&ldquo;handled {0}&rdquo;, ex.Message);}}

    盡管所有的任務都會完成,但是我們可以從第一個task那里看到錯誤。雖然它不是第一個拋出錯誤的,但是它是列表中的第一個任務。

    如果想要從所有的任務中獲取錯誤,那么有一個方式就是將其在try代碼塊外部進行聲明。然后檢查Task方法的“IsFaulted”屬性。如果有錯誤拋出,那么其“IsFaulted”屬性為true。

    示例如下:

    static async void ShowAggregatedException(){Task taskResult = null;try {Task<string> t1 = GreetingAsync("Bulbul");Task<string> t2 = GreetingAsync("Ahmed");await (taskResult = Task.WhenAll(t1, t2));}catch (Exception ex){Console.WriteLine("handled {0}", ex.Message);foreach (var innerEx in taskResult.Exception.InnerExceptions){Console.WriteLine("inner exception {0}", nnerEx.Message);}}}

    Task的取消執行

    如果直接使用ThreadPool中的Thread,我們是無法進行取消操作的。但是現在Task類提供了一種基于CancellationTokenSource類的方式來取消任務的執行,可以按照如下步驟來進行:

  • 異步方法需要附帶一個“CancellationToken”類型。
  • 創建CancellationTokenSource類的實例:
  • 將CancellationToken傳遞給異步方法:
  • 對于長時間執行的方法,如果想取消的話,我們需要調用CancellationToken的ThrowIfCancellationRequested方法。
  • 捕捉Task拋出的 OperationCanceledException。
  • 現在,如果我們通過調用CancellationTokenSource的cancel方法來取消當前的操作的話,對于那些長時間運行的操作,將會拋出OperationCanceledException錯誤。我們也可以通過設置超時時間來取消任務。更多關于CancellationTokenSource類的信息,請移步:https://msdn.microsoft.com/en-us/library/system.threading.cancellationtokensource%28v=vs.110%29.aspx
  • var cts = new CancellationTokenSource();Task<string> t1 = GreetingAsync("Bulbul", cts.Token); static string Greeting(string name, CancellationToken token) {Thread.Sleep(3000);token.ThrowIfCancellationRequested();return string.Format("Hello, {0}", name); }

    下面讓我們看看如何設置超時時間來取消任務的執行:

    static void Main(string[] args){CallWithAsync();Console.ReadKey(); }async static void CallWithAsync(){try{CancellationTokenSource source = new CancellationTokenSource();source.CancelAfter(TimeSpan.FromSeconds(1));var t1 = await GreetingAsync("Bulbul", source.Token);}catch (OperationCanceledException ex){Console.WriteLine(ex.Message);}}static Task<string> GreetingAsync(string name, CancellationToken token){return Task.Run<string>(() =>{return Greeting(name, token);});}static string Greeting(string name, CancellationToken token){Thread.Sleep(3000);token.ThrowIfCancellationRequested();return string.Format("Hello, {0}", name);}

    并行編程

    在.net 4.5中,存在一個叫做“Parallel”的類,這個類可以進行并行操作。當然這種并行和那些充分利用cpu計算能力的Thread 是有差別的,簡單說起來,它有兩種表現方式:

    1.數據并行。 如果我們有很多的數據需要計算,我們需要他們并行的進行,那么我們可以使用For或者ForEach方法來進行:

    ParallelLoopResult result =Parallel.For(0, 100, async (int i) =>{Console.WriteLine("{0}, task: {1}, thread: {2}", i,Task.CurrentId, Thread.CurrentThread.ManagedThreadId);await Task.Delay(10);});

    如果我們在計算的過程中,想要中斷并行,我們可以把ParallelLoopState當做參數傳遞進去,我們就可以實現認為和中斷這種循環:

    ParallelLoopResult result =Parallel.For(0, 100, async (int i, ParallelLoopState pls) =>{Console.WriteLine("{0}, task: {1}, thread: {2}", i,Task.CurrentId, Thread.CurrentThread.ManagedThreadId);await Task.Delay(10);if (i > 5) pls.Break();});

    需要注意的是,當我們需要中斷循環的時候,由于其運行在諸多個線程之上,如果線程數多于我們設定的中斷數時,上述的執行可能就不太準確。

  • 任務并行。如果我們想要多個任務并行處理,那么我們可以使用Parallel.Invoke方法來接受Action委托數組。例如:
  • static void ParallelInvoke() {Parallel.Invoke(MethodOne, MethodTwo); }

    ?參考文章:http://www.codeproject.com/Articles/996857/Asynchronous-programming-and-Threading-in-Csharp-N?bmkres=exist#_articleTop

    ?

    引用:https://www.cnblogs.com/scy251147/p/4597615.html

    ?

    ?

    異步編程總結

    ? ???最近在為公司的分布式服務框架做支持異步調用的開發,這種新特性的上線需要進行各種嚴格的測試。在并發性能測試時,性能一直非常差,而且非常的不穩定。經過不斷的分析調優,發現Socket通信和多線程異步回調存在較為嚴重的性能問題。經過多方優化,性能終于達標。下面是原版本、支持異步最初版本和優化后版本的性能比較。差異還是非常巨大的。另外說明一下,總耗時是指10000次請求累計執行時間。

    ???? 從上圖可以看到,支持異步的版本,在單線程模式下,性能的表現與老版本差異并不明顯,但是10線程下差異就非常巨大,而100線程的測試結果反而有所好轉。通過分析,兩個版本的性能差異如此巨大,主要是因為:

  • 同步模式會阻塞客戶端請求,說白了,在線程內就是串行請求的。但是在異步模式中,線程內的請求不再阻塞,網絡流量、后臺計算壓力瞬間暴漲,峰值是同步模式的100倍。網絡傳輸變成瓶頸點。
  • 在壓力暴漲的情況下,CPU資源占用也會突變, 并且ThreadPool、Task、異步調用的執行都將變慢。
  • ???? 在網絡通信方面,把原先半異步的模式調整為了SocketAsyncEventArgs?模式。下面是Socket通信的幾種模型的介紹和示例,總結一下,與大家分享。下次再與大家分享,并發下異步調用的性能優化方案。

    APM方式:?Asynchronous Programming Model

    ????異步編程模型是一種模式,該模式允許用更少的線程去做更多的操作,.NET Framework很多類也實現了該模式,同時我們也可以自定義類來實現該模式。NET Framework中的APM也稱為Begin/End模式。此種模式下,調用BeginXXX方法來啟動異步操作,然后返回一個IAsyncResult 對象。當操作執行完成后,系統會觸發IAsyncResult 對象的執行。 具體可參考:?https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/asynchronous-programming-model-apm

    ???? .net中的Socket異步模式也支持APM,與同步模式或Blocking模式相比,可以更好的利用網絡帶寬和系統資源編寫出具有更高性能的程序。參考具體代碼如下:

    服務端監聽:

    ????

    Socket serverSocket =?new?Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

    //本機預使用的IP和端口

    IPEndPoint serverIP =?new?IPEndPoint(IPAddress.Any, 9050);

    //綁定服務端設置的IP

    serverSocket.Bind(serverIP);

    //設置監聽個數

    serverSocket.Listen(1);

    //異步接收連接請求

    serverSocket.BeginAccept(ar =>

    {

    ??? base.communicateSocket = serverSocket.EndAccept(ar);

    ???AccessAciton();

    ?},?null);

    客戶端連接:

    var communicateSocket =?new?Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

    ???communicateSocket.Bind(new?IPEndPoint(IPAddress.Any, 9051));

    ?????????????

    ????????//服務器的IP和端口

    ????????IPEndPoint serverIP;

    ????????try

    ????????{

    ????????????serverIP =?new?IPEndPoint(IPAddress.Parse(IP), 9050);

    ????????}

    ????????catch

    ????????{

    ????????????throw?new?Exception(String.Format("{0}不是一個有效的IP地址!", IP));

    ????????}

    ?????????????

    ????????//客戶端只用來向指定的服務器發送信息,不需要綁定本機的IP和端口,不需要監聽

    ????????try

    ????????{

    ?????????? communicateSocket.BeginConnect(serverIP, ar =>

    ????????????{

    ????????????????AccessAciton();

    ????????????},?null);

    ????????}

    ????????catch

    ????????{

    ????????????throw?new?Exception(string.Format("嘗試連接{0}不成功!", IP));

    ????????}

    客戶端請求:

    ????

    if?(communicateSocket.Connected ==?false)

    ????????{

    ????????????throw?new?Exception("還沒有建立連接, 不能發送消息");

    ????????}

    ????????Byte[] msg = Encoding.UTF8.GetBytes(message);

    ????????communicateSocket.BeginSend(msg,0, msg.Length, SocketFlags.None,

    ????????????ar => {

    ?????????????????

    ????????????},?null);

    ?

    服務端響應:

    Byte[] msg =?new?byte[1024];

    ????????//異步的接受消息

    ????????communicateSocket.BeginReceive(msg, 0, msg.Length, SocketFlags.None,

    ????????????ar => {

    ????????????????//對方斷開連接時, 這里拋出Socket Exception??????????????

    ????????????????????communicateSocket.EndReceive(ar);

    ????????????????ReceiveAction(Encoding.UTF8.GetString(msg).Trim('\0',' '));

    ????????????????Receive(ReceiveAction);

    ????????????},?null);

    ?

    ??????注意:異步模式雖好,但是如果進行大量異步套接字操作,是要付出很高代價的。針對每次操作,都必須創建一個IAsyncResult對象,而且該對象不能被重復使用。由于大量使用對象分配和垃圾收集,這會影響系統性能。如需要更好的理解APM模式,最了解EAP模式:Event-based Asynchronous Pattern:https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/event-based-asynchronous-pattern-eap?。

    ?

    TAP 方式:?Task-based Asynchronous Pattern

    ??????基于任務的異步模式,該模式主要使用System.Threading.Tasks.Task和Task<T>類來完成異步編程,相對于APM?模式來講,TAP使異步編程模式更加簡單(因為這里我們只需要關注Task這個類的使用),同時TAP也是微軟推薦使用的異步編程模式。APM與TAP的本質區別,請參考我的一篇歷史博客:http://www.cnblogs.com/vveiliang/p/7943003.html

    ???? TAP模式與APM模式是兩種異步模式的實現,從性能上看沒有本質的差別。TAP的資料可參考:https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap?。參考具體代碼如下:

    服務端:

    publicclassStateContext

    {

    ???// Client socket.???

    ???publicSocketWorkSocket =null;

    ???// Size of receive buffer.???

    ???publicconstintBufferSize = 1024;

    ???// Receive buffer.???

    ???publicbyte[] buffer =newbyte[BufferSize];

    ???// Received data string.???

    ???publicStringBuildersb =newStringBuilder(100);

    }

    publicclassAsynchronousSocketListener

    {

    ???// Thread signal.???

    ???publicstaticManualResetEventreSetEvent =newManualResetEvent(false);

    ???publicAsynchronousSocketListener()

    ??? {

    ??? }

    ???publicstaticvoidStartListening()

    ??? {

    ???????// Data buffer for incoming data.???

    ???????byte[] bytes =newByte[1024];

    ???????// Establish the local endpoint for the socket.???

    ???????IPAddressipAddress =IPAddress.Parse("127.0.0.1");

    ???????IPEndPointlocalEndPoint =newIPEndPoint(ipAddress, 11000);

    ???????// Create a TCP/IP socket.???

    ???????Socketlistener =newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);

    ???????// Bind the socket to the local???

    ???????try

    ??????? {

    ??????????? listener.Bind(localEndPoint);

    ??????????? listener.Listen(100);

    ???????????while(true)

    ??????????? {

    ???????????????// Set the event to nonsignaled state.???

    ??????????????? reSetEvent.Reset();

    ???????????????// Start an asynchronous socket to listen for connections.???

    ???????????????Console.WriteLine("Waiting for a connection...");

    ??????????????? listener.BeginAccept(newAsyncCallback(AcceptCallback), listener);

    ???????????????// Wait until a connection is made before continuing.???

    ??????????????? reSetEvent.WaitOne();

    ??????????? }

    ??????? }

    ???????catch(Exceptione)

    ??????? {

    ???????????Console.WriteLine(e.ToString());

    ??????? }

    ???????Console.WriteLine("\nPress ENTER to continue...");

    ???????Console.Read();

    ??? }

    ???publicstaticvoidAcceptCallback(IAsyncResultar)

    ??? {

    ???????// Signal the main thread to continue.???

    ??????? reSetEvent.Set();

    ???????// Get the socket that handles the client request.???

    ???????Socketlistener = (Socket)ar.AsyncState;

    ???????Sockethandler = listener.EndAccept(ar);

    ???????// Create the state object.???

    ???????StateContextstate =newStateContext();

    ??????? state.WorkSocket = handler;

    ??????? handler.BeginReceive(state.buffer, 0,StateContext.BufferSize, 0,newAsyncCallback(ReadCallback), state);

    ??? }

    ???publicstaticvoidReadCallback(IAsyncResultar)

    ??? {

    ???????Stringcontent =String.Empty;

    ???????StateContextstate = (StateContext)ar.AsyncState;

    ???????Sockethandler = state.WorkSocket;

    ???????// Read data from the client socket.???

    ???????intbytesRead = handler.EndReceive(ar);

    ???????if(bytesRead > 0)

    ??????? {

    ???????????// There might be more data, so store the data received so far.???

    ??????????? state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));

    ???????????// Check for end-of-file tag. If it is not there, read???

    ???????????// more data.???

    ??????????? content = state.sb.ToString();

    ???????????if(content.IndexOf("<EOF>") > -1)

    ??????????? {

    ???????????????Console.WriteLine("讀取 {0} bytes. \n 數據: {1}", content.Length, content);

    ??????????????? Send(handler, content);

    ??????????? }

    ???????????else

    ??????????? {

    ??????????????? handler.BeginReceive(state.buffer, 0,StateContext.BufferSize, 0,newAsyncCallback(ReadCallback), state);

    ??????????? }

    ??????? }

    ??? }

    ???privatestaticvoidSend(Sockethandler,Stringdata)

    ??? {

    ???????byte[] byteData =Encoding.ASCII.GetBytes(data);

    ??????? handler.BeginSend(byteData, 0, byteData.Length, 0,newAsyncCallback(SendCallback), handler);

    ??? }

    ???privatestaticvoidSendCallback(IAsyncResultar)

    ??? {

    ???????try

    ??????? {

    ???????????Sockethandler = (Socket)ar.AsyncState;

    ???????????intbytesSent = handler.EndSend(ar);

    ???????????Console.WriteLine("發送 {0} bytes.", bytesSent);

    ??????????? handler.Shutdown(SocketShutdown.Both);

    ??????????? handler.Close();

    ??????? }

    ???????catch(Exceptione)

    ??????? {

    ???????????Console.WriteLine(e.ToString());

    ??????? }

    ??? }

    ???publicstaticintMain(String[] args)

    ??? {

    ??????? StartListening();

    ???????return0;

    ??? }

    客戶端:

    publicclassAsynchronousClient

    {

    ???// The port number for the remote device.???

    ???privateconstintport = 11000;

    ???// ManualResetEvent instances signal completion.???

    ???privatestaticManualResetEventconnectResetEvent =newManualResetEvent(false);

    ???privatestaticManualResetEventsendResetEvent =newManualResetEvent(false);

    ???privatestaticManualResetEventreceiveResetEvent =newManualResetEvent(false);

    ???privatestaticStringresponse =String.Empty;

    ???privatestaticvoidStartClient()

    ??? {

    ???????try

    ??????? {

    ?????????

    ???????????IPAddressipAddress =IPAddress.Parse("127.0.0.1");

    ???????????IPEndPointremoteEP =newIPEndPoint(ipAddress, port);

    ???????????// Create a TCP/IP socket.???

    ???????????Socketclient =newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);

    ???????????// Connect to the remote endpoint.???

    ??????????? client.BeginConnect(remoteEP,newAsyncCallback(ConnectCallback), client);

    ??????????? connectResetEvent.WaitOne();

    ??????????? Send(client,"This is a test<EOF>");

    ??????????? sendResetEvent.WaitOne();

    ??????????? Receive(client);

    ??????????? receiveResetEvent.WaitOne();

    ???????????Console.WriteLine("Response received : {0}", response);

    ???????????// Release the socket.???

    ??????????? client.Shutdown(SocketShutdown.Both);

    ??????????? client.Close();

    ???????????Console.ReadLine();

    ??????? }

    ???????catch(Exceptione)

    ??????? {

    ???????????Console.WriteLine(e.ToString());

    ??????? }

    ??? }

    ???privatestaticvoidConnectCallback(IAsyncResultar)

    ??? {

    ???????try

    ??????? {

    ???????????Socketclient = (Socket)ar.AsyncState;

    ??????????? client.EndConnect(ar);

    ???????????Console.WriteLine("Socket connected to {0}", client.RemoteEndPoint.ToString());

    ??????????? connectResetEvent.Set();

    ??????? }

    ???????catch(Exceptione)

    ??????? {

    ???????????Console.WriteLine(e.ToString());

    ??????? }

    ??? }

    ???privatestaticvoidReceive(Socketclient)

    ??? {

    ???????try

    ??????? {

    ???????????StateContextstate =newStateContext();

    ??????????? state.WorkSocket = client;

    ??????????? client.BeginReceive(state.buffer, 0,StateContext.BufferSize, 0,newAsyncCallback(ReceiveCallback), state);

    ??????? }

    ???????catch(Exceptione)

    ??????? {

    ???????????Console.WriteLine(e.ToString());

    ??????? }

    ??? }

    ???privatestaticvoidReceiveCallback(IAsyncResultar)

    ??? {

    ???????try

    ??????? {

    ???????????StateContextstate = (StateContext)ar.AsyncState;

    ???????????Socketclient = state.WorkSocket;

    ???????????intbytesRead = client.EndReceive(ar);

    ???????????if(bytesRead > 0)

    ??????????? {

    ??????????????? state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));

    ??????????????? client.BeginReceive(state.buffer, 0,StateContext.BufferSize, 0,newAsyncCallback(ReceiveCallback), state);

    ??????????? }

    ???????????else

    ??????????? {

    ???????????????if(state.sb.Length > 1)

    ??????????????? {

    ??????????????????? response = state.sb.ToString();

    ??????????????? }

    ??????????????? receiveResetEvent.Set();

    ??????????? }

    ??????? }

    ???????catch(Exceptione)

    ??????? {

    ???????????Console.WriteLine(e.ToString());

    ??????? }

    ??? }

    ???privatestaticvoidSend(Socketclient,Stringdata)

    ??? {

    ???????byte[] byteData =Encoding.ASCII.GetBytes(data);

    ??????? client.BeginSend(byteData, 0, byteData.Length, 0,newAsyncCallback(SendCallback), client);

    ??? }

    ???privatestaticvoidSendCallback(IAsyncResultar)

    ??? {

    ???????try

    ??????? {

    ???????????Socketclient = (Socket)ar.AsyncState;

    ???????????intbytesSent = client.EndSend(ar);

    ???????????Console.WriteLine("Sent {0} bytes to server.", bytesSent);

    ??????????? sendResetEvent.Set();

    ??????? }

    ???????catch(Exceptione)

    ??????? {

    ???????????Console.WriteLine(e.ToString());

    ??????? }

    ??? }

    ???publicstaticintMain(String[] args)

    ??? {

    ??????? StartClient();

    ???????return0;

    ??? }

    }

    SAEA方式:?SocketAsyncEventArgs

    ????? APM模式、TAP模式雖然解決了Socket的并發問題,但是在大并發下還是有較大性能問題的。這主要是因為上述兩種模式都會生產 IAsyncResult 等對象 ,而大量垃圾對象的回收會非常影響系統的性能。為此,微軟推出了?SocketAsyncEventArgs?。SocketAsyncEventArgs 是?.NET Framework 3.5?開始支持的一種支持高性能 Socket 通信的實現。SocketAsyncEventArgs 相比于 APM 方式的主要優點可以描述如下,無需每次調用都生成 IAsyncResult 等對象,向原生 Socket 更靠近一些。這是官方的解釋:

    The main feature of these enhancements is the?avoidance of the repeated allocation and synchronization of objects?during high-volume asynchronous socket I/O. The Begin/End design pattern currently implemented by the Socket class for asynchronous socket I/O requires a System.IAsyncResult object be allocated for each asynchronous socket operation.

    ????? SocketAsyncEventArgs主要為高性能網絡服務器應用程序而設計,避免了在異步套接字 I/O 量非常大時,大量垃圾對象創建與回收。使用此類執行異步套接字操作的模式包含以下步驟,具體說明可參考:https://msdn.microsoft.com/en-us/library/system.net.sockets.socketasynceventargs(v=vs.110).aspx?。

  • 分配一個新的 SocketAsyncEventArgs 上下文對象,或者從應用程序池中獲取一個空閑的此類對象。
  • 將該上下文對象的屬性設置為要執行的操作(例如,完成回調方法、數據緩沖區、緩沖區偏移量以及要傳輸的最大數據量)。
  • 調用適當的套接字方法 (xxxAsync) 以啟動異步操作。
  • 如果異步套接字方法 (xxxAsync) 返回 true,則在回調中查詢上下文屬性來獲取完成狀態。
  • 如果異步套接字方法 (xxxAsync) 返回 false,則說明操作是同步完成的。 可以查詢上下文屬性來獲取操作結果。
  • 將該上下文重用于另一個操作,將它放回到應用程序池中,或者將它丟棄。
  • ??? 下面是封裝的一個組件代碼:

    classBufferManager

    ??? {

    ???????intm_numBytes;????????????????// the total number of bytes controlled by the buffer pool

    ???????byte[] m_buffer;???????????????// the underlying byte array maintained by the Buffer Manager

    ???????Stack<int> m_freeIndexPool;????//

    ???????intm_currentIndex;

    ???????intm_bufferSize;

    ???????publicBufferManager(inttotalBytes,intbufferSize)

    ??????? {

    ??????????? m_numBytes = totalBytes;

    ??????????? m_currentIndex = 0;

    ??????????? m_bufferSize = bufferSize;

    ??????????? m_freeIndexPool =newStack<int>();

    ??????? }

    ???????// Allocates buffer space used by the buffer pool

    ???????publicvoidInitBuffer()

    ??????? {

    ???????????// create one big large buffer and divide that

    ???????????// out to each SocketAsyncEventArg object

    ??????????? m_buffer =newbyte[m_numBytes];

    ??????? }

    ???????// Assigns a buffer from the buffer pool to the

    ???????// specified SocketAsyncEventArgs object

    ???????//

    ???????// <returns>true if the buffer was successfully set, else false</returns>

    ???????publicboolSetBuffer(SocketAsyncEventArgsargs)

    ??????? {

    ???????????if(m_freeIndexPool.Count > 0)

    ??????????? {

    ??????????????? args.SetBuffer(m_buffer, m_freeIndexPool.Pop(), m_bufferSize);

    ??????????? }

    ???????????else

    ??????????? {

    ???????????????if((m_numBytes - m_bufferSize) < m_currentIndex)

    ??????????????? {

    ???????????????????returnfalse;

    ??????????????? }

    ??????????????? args.SetBuffer(m_buffer, m_currentIndex, m_bufferSize);

    ??????????????? m_currentIndex += m_bufferSize;

    ??????????? }

    ???????????returntrue;

    ??????? }

    ???????// Removes the buffer from a SocketAsyncEventArg object.

    ???????// This frees the buffer back to the buffer pool

    ???????publicvoidFreeBuffer(SocketAsyncEventArgsargs)

    ??????? {

    ??????????? m_freeIndexPool.Push(args.Offset);

    ??????????? args.SetBuffer(null, 0, 0);

    ??????? }

    ??? }

    ???///<summary>

    ???///This class is used to communicate with a remote application over TCP/IP protocol.

    ???///</summary>

    ???classTcpCommunicationChannel

    ??? {

    ??????

    ???????#regionPrivate fields

    ???????///<summary>

    ???????///Size of the buffer that is used to receive bytes from TCP socket.

    ???????///</summary>

    ???????privateconstintReceiveBufferSize = 8 * 1024;//4KB

    ???????///<summary>

    ???????///This buffer is used to receive bytes

    ???????///</summary>

    ???????privatereadonlybyte[] _buffer;

    ???????///<summary>

    ???????///Socket object to send/reveice messages.

    ???????///</summary>

    ???????privatereadonlySocket_clientSocket;

    ???????///<summary>

    ???????///A flag to control thread's running

    ???????///</summary>

    ???????privatevolatilebool_running;

    ???????///<summary>

    ???????///This object is just used for thread synchronizing (locking).

    ???????///</summary>

    ???????privatereadonlyobject_syncLock;

    ???????privateBufferManagerreceiveBufferManager;

    ???????privateSocketAsyncEventArgsreceiveBuff =null;

    ???????#endregion

    ???????#regionConstructor

    ???????///<summary>

    ???????///Creates a new TcpCommunicationChannel object.

    ???????///</summary>

    ???????///<param name="clientSocket">A connected Socket object that is

    ???????///used to communicate over network</param>

    ???????publicTcpCommunicationChannel(SocketclientSocket)

    ??????? {

    ??????????? _clientSocket = clientSocket;

    ??????????? _clientSocket.Blocking =false;

    ??????????? _buffer =newbyte[ReceiveBufferSize];

    ??????????? _syncLock =newobject();

    ??????????? Init();

    ??????? }

    ???????privatevoidInit()

    ??????? {

    ???????????//初始化接收Socket緩存數據

    ??????????? receiveBufferManager =newBufferManager(ReceiveBufferSize*2, ReceiveBufferSize);

    ??????????? receiveBufferManager.InitBuffer();

    ??????????? receiveBuff =newSocketAsyncEventArgs();

    ??????????? receiveBuff.Completed += ReceiveIO_Completed;

    ??????????? receiveBufferManager.SetBuffer(receiveBuff);

    ???????????//初始化發送Socket緩存數據

    ??????? }

    ???????#endregion

    ???????#regionPublic methods

    ???????///<summary>

    ???????///Disconnects from remote application and closes channel.

    ???????///</summary>

    ???????publicvoidDisconnect()

    ??????? {

    ??????????? _running =false;

    ??????????? receiveBuff.Completed -= ReceiveIO_Completed;

    ??????????? receiveBuff.Dispose();

    ???????????if(_clientSocket.Connected)

    ??????????? {

    ??????????????? _clientSocket.Close();

    ??????????? }

    ??????????? _clientSocket.Dispose();

    ??????? }

    ???????#endregion

    ?????

    ???????publicvoidStartReceive()

    ??????? {

    ??????????? _running =true;

    ???????????boolresult = _clientSocket.ReceiveAsync(receiveBuff);

    ??????? }

    ???????privatevoidReceiveIO_Completed(objectsender,SocketAsyncEventArgse)

    ??????? {

    ???????????if(e.BytesTransferred > 0 && e.SocketError ==SocketError.Success && _clientSocket.Connected ==true&& e.LastOperation ==SocketAsyncOperation.Receive)

    ??????????? {

    ???????????????if(!_running)

    ??????????????? {

    ???????????????????return;

    ??????????????? }

    ???????????????//Get received bytes count

    ???????????????DateTimereceiveTime =DateTime.Now;

    ???????????????//Copy received bytes to a new byte array

    ???????????????varreceivedBytes =newbyte[e.BytesTransferred];

    ???????????????Array.Copy(e.Buffer, 0, receivedBytes, 0, e.BytesTransferred);

    ???????????????//處理消息....

    ???????????????if(_running)

    ??????????????? {

    ??????????????????? StartReceive();

    ??????????????? }

    ??????????? }

    ??????? }

    ???????///<summary>

    ???????///Sends a message to the remote application.

    ???????///</summary>

    ???????///<param name="message">Message to be sent</param>

    ???????publicvoidSendMessage(byte[] messageBytes)

    ??????? {

    ???????????//Send message

    ???????????if(_clientSocket.Connected)

    ??????????? {

    ???????????????SocketAsyncEventArgsdata =newSocketAsyncEventArgs();

    ??????????????? data.SocketFlags =SocketFlags.None;

    ??????????????? data.Completed += (s, e) =>

    ??????????????? {

    ??????????????????? e.Dispose();

    ??????????????? };

    ??????????????? data.SetBuffer(messageBytes, 0, messageBytes.Length);

    ???????????????//Console.WriteLine("發送:" + messageBytes.LongLength);

    ??????????????? _clientSocket.SendAsync(data);

    ??????????? }

    ??????? }

    ??? }

    總結

    以上是生活随笔為你收集整理的【转】.Net中的异步编程总结的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。