.NET中的並行處理,並發和異步編程。


網上看了很多異步的方式,各種方式都有,梳理下.NET中編寫異步的方式,避免混淆。.NET提供的異步方式可以歸納為三種:.NET中的並行處理,並發和異步編程。在梳理.NET中的並行處理,並發和異步編程之前,先來了解下同步、異步、並行、並發等概念

一、異步編程中涉及的概念

1、同步(Synchronous)和異步(Asynchronous)

同步和異步的本質區別是是否需要等待,比如一個方法要執行,必須等前面一個方法程執行完成,才可以執行,這就是同步。如果不需要等上一個方法執行完成,並行或者並發執行,這就是異步調用。

2、並發(Concurrency)和並行(Parallelism)

  • 並行才是真正意義上的並行執行,並發只是線程的交替執行,有可能存在串行的情況。在單核CPU的系統,線程只能是並發的,而不能支持並行,並行執行只能存在與多核CPU的系統。並行(parallel):指在同一時刻,有多條指令在多個處理器上同時執行。所以無論從微觀還是從宏觀來看,二者都是一起執行的。

 

  • 並發(concurrency):指在同一時刻只能有一條指令執行,但多個進程指令被快速的輪換執行,使得在宏觀上具有多個進程同時執行的效果,但在微觀上並不是同時執行的,只是把時間分成若干段,使多個進程快速交替的執行。

 

3、臨界區

臨界區,可以理解為公共的資源或者說共享數據。臨界區具有保護性,也就是說,只能一個線程占用臨界區,一旦一個線程占了臨界區,另外一個線程是不予許再占用的,必須等線程釋放了才行。

4、阻塞(Blocking)和非阻塞(Non-Blocking)

阻塞是線程的一種比較嚴重的情況,從前面我們知道了臨界區只能允許一個線程占用,假如一個線程因為執行時間過長,占用了臨界區,不掛起,其它想要占用臨界區的線程只能等待,這種情況就容易造成線程阻塞。非阻塞的話就相反了,指所有線程都正常執行,不會出現線程占臨界區不掛起的情況。

5、飢餓(Starvation)、死鎖(Deadlock)和活鎖(Livelock)  

飢餓,有些情況可能是一個線程優先級太低了,每次都被其它線程占用了,導致該線程一直不能占用臨界區。也有一些情況是上一個線程執行時間太長了,一直沒釋放,導致其它線程都不能占用臨界區,這也是造成線程飢餓。死鎖有可能是因為線程死循環調用等等情況造成的,一旦出現這種情況估計就得人工排查了。活鎖,是因為線程互相掛起臨界區,給其它線程用,互相“謙讓”,導致資源在兩個或者幾個線程之間跳到,這種情況就是活鎖。

 

二、異步編程 

.NET提供了三種用於執行異步操作的模式:

  • 基於任務的異步模式(TAP),它使用一種方法來表示異步操作的啟動和完成。TAP是在.NET Framework 4中引入的。這是.NET 中異步編程的推薦方法。C#中的asyncawait關鍵字以及Visual Basic中的AsyncAwait運算符添加了對TAP的語言支持。

  • 基於事件的異步模式(EAP),這是用於提供異步行為的基於事件的舊模型。它需要一種具有Async后綴和一個或多個事件,事件處理程序委托類型以及EventArg派生類型的方法。EAP是在.NET Framework 2.0中引入的。不再推薦用於新開發。

  • 異步編程模型(APM)模式(也稱為IAsyncResult模式),這是使用IAsyncResult接口提供異步行為的舊模型。在這種模式下,同步操作需要BeginEnd方法(例如,BeginWriteEndWrite實現異步寫操作)。對於新的開發,不再建議使用此模式。

1、APM模式

(1)APM詳解

APM是微軟利用委托和線程池幫助我們實現的一個模式,該模式利用一個線程池線程去執行一個操作,在FileStream類BeginRead方法中就是執行一個讀取文件操作,該線程池線程會立即將控制權返回給調用線程,此時線程池線程在后台進行這個異步操作;異步操作完成之后,通過回調函數來獲取異步操作返回的結果。此時就是利用委托的機制。所以說APM是利用委托和線程池線程搞出來的模式,包括后面的基於事件的異步編程和基於任務的異步編程,還有C# 5中的async和await關鍵字,都是利用這委托和線程池搞出來的。他們的本質都一樣,后面方法使異步編程更加簡單。.NET1.0時期就提出的一種異步模式,並且基於IAsyncResult接口實現BeginXXX和EndXXX類似的方法。.net中有很多類實現了該模式(比如HttpWebRequest),同時我們也可以自定義類來實現APM模式(繼承IAsyncResult接口並且實現BeginXXX和EndXXX方法)

(2)HttpWebRequest示例

using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;

namespace APMDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            string url = "https://www.baidu.com/";
            var request = HttpWebRequest.Create(url);
            request.BeginGetResponse(AsyncCallbackMethod, request);//BeginGetResponse,發起異步請求
            Console.WriteLine($"運行主線程");
            Console.ReadKey();
        }

        /// <summary>
        /// 回調方法
        /// </summary>
        /// <param name="asyncResult"></param>
        public static void AsyncCallbackMethod(IAsyncResult asyncResult)
        {
            HttpWebRequest request = asyncResult.AsyncState as HttpWebRequest;
            var response = request.EndGetResponse(asyncResult);//EndGetResponse異步請求完成
            var stream = response.GetResponseStream();
            StringBuilder sb = new StringBuilder();
            Console.WriteLine($"當前線程Id:{Thread.CurrentThread.ManagedThreadId}");
            using (StreamReader reader = new StreamReader(stream))
            {
                var content = reader.ReadLine();
                sb.AppendLine(content);
                Console.WriteLine($"結果:{sb.ToString()}");
            }
        }
    }
}

執行結果:

2、EAP模式

(1)詳解

EAP是.net 2.0提出的,實現了基於事件的異步模式的類將具有一個或者多個以Async為后綴的方法和對應的Completed事件,並且這些類都支持異步方法的取消、進度報告和報告結果。然而.net中並不是所有的類都支持EAP。當調用基於事件的EAP模式的類的XXXAsync方法時,就開始了一個異步操作,並且基於事件的EAP模式是基於APM模式之上的,而APM又是建立在委托之上的。下面的Demo就以BackgroundWorker類來演示如何使用EAP異步。

(2)示例

using System;
using System.ComponentModel;

namespace EAPDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            EAPManage.GetStudentAsync(4, student =>
            {
                Console.WriteLine($"學生姓名:{student.Name}");
            });
            Console.WriteLine($"主線程中");
            Console.ReadKey();
        }
    }

    public class EAPManage
    {
        /// <summary>
        /// 獲取信息
        /// </summary>
        /// <returns></returns>
        private static Student GetStudent(int stuID)
        {
            return new Student
            {
                Id = stuID,
                Name = "xiaoming"
            };
        }

        /// <summary>
        /// (async)
        /// </summary>
        /// <param name="userItem">contain: Guid(required)</param>
        /// <param name="handler">callback</param>
        public static void GetStudentAsync(int stuID, GetStudentHandler handler)
        {
            var background = new BackgroundWorker();
            background.DoWork += (sender, e) => { e.Result = GetStudent(stuID); };
            background.RunWorkerCompleted += (sender, e) =>
            {
                if (handler == null || e.Result == null || e.Result.GetType() != typeof(Student)) return;
                handler(e.Result as Student);
            };
            background.RunWorkerAsync();
        }
        public delegate void GetStudentHandler(Student student);
    }

    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
}

執行結果:

3、TAP模式

(1)詳解

.NET4.0提出了基於任務的異步編程模型,使用 Async 和 Await 的異步編程

  • 方法簽名包含 async 修飾符,按照約定,異步方法的名稱以“Async”后綴結尾
  • 方法通常包含至少一個 await 表達式,該表達式標記一個點,在該點上,直到等待的異步操作完成方法才能繼續。 同時,將方法掛起,並且控制權返回到方法的調用方。
  • 異步方法在 await 表達式執行時暫停並不構成方法退出,只會導致 finally 代碼塊不運行
  • 標記的異步方法本身可以通過調用它的方法等待
  • 異步方法通常包含 await 運算符的一個或多個實例,但缺少 await 表達式也不會導致生成編譯器錯誤。 如果異步方法未使用 await 運算符標記暫停點,那么異步方法即使有 async 修飾符也會作為同步方法執行,編譯器將為此類方法發布一個警告

三、並行編程

並行開發目前還沒有詳細了解過,這里搜羅了比較好的文章,有時間研究下

  https://www.cnblogs.com/stoneniqiu/category/749413.html

  https://www.cnblogs.com/huangxincheng/category/368987.html

 


免責聲明!

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



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