net 非同步與同步

来源:https://www.cnblogs.com/wangbaicheng1477865665/archive/2018/04/23/net_async1.html
-Advertisement-
Play Games

本文是一篇介紹net同步非同步的文章,是為張四火同學原創的。請張四活同學及廣大讀者指出,文章不通順的地方。後續應當還有兩篇文章敬請期待 ...


 

 

 

一、摘論

 為什麼不是摘要呢?其實這個是我個人的想法,其實很多人在談論非同步與同步的時候都忽略了,同步非同步不是軟體的原理,其本身是電腦的原理及概念,這裡就不過多的闡述電腦原理了。在學習同步與非同步之前,我們需要先研究幾個問題

在說到非同步前,先來理一下幾個容易混淆的概念,並行、多線程、非同步。

    並行,一般指並行計算,是說同一時刻有多條指令同時被執行,這些指令可能執行於同一CPU的多核上,或者多個CPU上,或者多個物理主機甚至多個網路中。

    多線程,一般指同一進程中多個線程(包含其數據結構、上下文與代碼片段)協作運行。在多核電腦中多個線程將有機會同時運行於多個核上,如果線程中進行的是計算,則行成並行計算。

    非同步,與同步相對應,是指呼叫另一操作後,不等待其結果,繼續執行之後的操作,若之後沒有其他操作,當前線程將進入睡眠狀態,而CPU時間將有機會切至其他線程。在非同步操作完成後通過回調函數的方式獲取通知與結果。非同步的實現方式有多種,如多線程與完成埠。多線程將非同步操作放入另一線程中運行,通過輪詢或回調方法得到完成通知;完成埠,由操作系統接管非同步操作的調度,通過硬體中斷,在完成時觸發回調方法,此方式不需要占用額外線程。

通過上面的兩張圖,可以把三個概念透析的非常好理解,非同步在某種意義上講是“時空轉換”即時間換空間,空間換時間。下邊我們來學習下,在net 中的非同步

 

二、同步和非同步

1.同步執行

為了準備一個耗時的程式,本人準備了一本Txt修仙小說,我們用程式讀取一行行輸出,輸出完成以後,我們輸出一句話,"今天書就讀到這裡吧!!累了,休息一會,休息一會!一休哥",為了更好的演示同步非同步,本文采用winform程式,同時為了體驗winform 和控制台 帶來的視覺效果,我們選擇項目屬性,應用程式這選擇控制台。

在準備一個很費時的讀書方法,

  /// <summary>
        /// 讀書,一個很廢時間的任務
        /// </summary>
        public void ReadBook()
        {
            //我們可以通過 Thread.CurrentThread.ManagedThreadId 獲取當前線程的唯一標識符
            Console.WriteLine("********************** ReadBook Start【" + Thread.CurrentThread.ManagedThreadId + "】等待............... **********************************************");
            System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
            watch.Start();
            string Path= AppDomain.CurrentDomain.BaseDirectory + "zqjz.txt";
            List<string> list = new List<string>();
            System.IO.StreamReader sr = new System.IO.StreamReader(Path, Encoding.Default);
            string line = "";
            Console.ForegroundColor = ConsoleColor.Black;
            while ((line = sr.ReadLine()) != null&& list.Count<120)
            {
              char[] array=  line.ToArray();
                for (int i = 0; i < array.Length; i++)
                {
                  
                    Console.Write(array[i]);
                    if (i!=0)
                    {
                        //  Thread.Sleep(128);//人眼最快敏感視覺是128毫秒左右,我們這裡測試先使用10毫秒
                        Thread.Sleep(10);
                    }
                }
                Console.WriteLine();
                Console.BackgroundColor = (ConsoleColor)new Random().Next(3, 15);
                list.Add(line);
            }
            sr.Close();
            sr.Dispose();
            watch.Stop();
            Console.WriteLine("今天讀書用了"+ watch.ElapsedMilliseconds+"豪秒");
            Console.WriteLine("********************** ReadBook End【" + Thread.CurrentThread.ManagedThreadId + " 】**********************************************");
        }

 這個方法比較簡單,就是讀取電子書,同時給方法加上了耗時記錄,和當前線程的唯一標識。現在我們在窗體上加上一個buttion 調用下我們的讀書。看看結果是怎麼樣的,同時建議打開任務管理器,監控下CPU,等cpu 平穩以後,我們在點擊同步執行按鈕。“現在是我們自己在讀書”。

2.非同步執行

關於非同步在前邊的摘論裡面介紹了大概,這裡不過多演示,請繼續看!在早期,net 的非同步都是在使用委托來做的,而委托使用的是線程池ThreadPool來實現的,曾取下一篇文章介紹線程,到時候在詳細介紹線程池,關於委托請觀看本人前邊的文章 "linq to Objet",我們在程式上在加上一個按鈕,裡面老師讀書,我的心缺飛了,在想下課玩什麼?怎麼和同學玩。

 private void btnSync_Click(object sender, EventArgs e)
        {//同步
            Console.WriteLine("**********************btnSync_Click Start【" + Thread.CurrentThread.ManagedThreadId + "】**********************************************");
            ReadBook();
            MessageBox.Show("今天書就讀到這裡吧!!累了,休息一會,休息一會!一休哥");
            Console.WriteLine("**********************btnSync_Click End 【" + Thread.CurrentThread.ManagedThreadId + "】**********************************************");
        }
        private void btnasync_Click(object sender, EventArgs e)
        {//非同步
            Console.WriteLine("**********************btnSync_Click Start【" + Thread.CurrentThread.ManagedThreadId + "】**********************************************");
            Action action = new Action(() => ReadBook());
            action.BeginInvoke(null,null);//參數先不管,我們先給null,一會我們會繼續演示
            MessageBox.Show("今天想玩,怎麼騙過老師呢!!書還在繼續讀,但是我已經在玩了!!!");
            Console.WriteLine("**********************btnSync_Click End 【" + Thread.CurrentThread.ManagedThreadId + "】**********************************************");
        }

 

 上面代碼分別為非同步調用和同步調用,下圖為非同步調用結果,我們會發現,非同步調用窗體是可以移動的,並且CPU 會有很大的波峰,細心的人會發現,執行時間是一樣的,只是他們的線程唯一標識是不一樣的。

 

 

 通過上述演示,非同步和同步的區別很簡單了吧!這裡就不過多描述,自己總結。但是我們的要說下非同步和多線程的區別?其實非同步只是一種結果(目地),而多線程才是實現這種結果的一種方式,在NET 裡面,非同步和多線程沒有本質的區別,個人總結唯一的區別就是,應用場景不同。

 重點:多播委托不可以指定非同步。不予顯示,自己去嘗試和找尋原理,實在找不到原理可以理解為這是任何高級語言的一個規定。有關多播委托請參考本人:一步一步帶你瞭解 Linq to Object

三、非同步回掉和非同步等待(阻塞)

 1.非同步回掉:

 剛纔我們一直在上課讀書,但是我的心裡在想的是下課去哪裡玩,如何玩?這個時候,我們需要在非同步讀書的方法之後也就是下課以後再去玩。看下代碼是怎麼寫的。

//非同步
            Console.WriteLine("**********************btnSync_Click Start【" + Thread.CurrentThread.ManagedThreadId + "】**********************************************");
            #region  非同步回調
            IAsyncResult iAsyncResult = null;
            AsyncCallback callback = t =>
            {
                Console.WriteLine(t);
                Console.WriteLine("下邊代碼是比較兩個對象是否一樣");
                Console.WriteLine($"string.ReferenceEquals(t, iAsyncResult)={string.ReferenceEquals(t, iAsyncResult)}");
                Console.WriteLine($"當前線程ID {Thread.CurrentThread.ManagedThreadId}");
                Console.WriteLine($"終於下課了!我們走吧,盡情的玩吧,你問我老師講的啥,我知道!!");
            };//AsyncCallback 本身就是一個委托,這個委托有一個參數,這個參數就是我們委托的BeginInvoke的返回值。我們使用這個委托去做非同步回調
            #endregion
            Action action = () => ReadBook();//簡寫
            iAsyncResult= action.BeginInvoke(callback, null);//這裡的第一個參數,我們就是非同步回調
            MessageBox.Show("今天想玩,怎麼騙過老師呢,下課玩點什麼呢!!書還在繼續讀,但是我的心已經飛了!!!");
            
            Console.WriteLine("**********************btnSync_Click End 【" + Thread.CurrentThread.ManagedThreadId + "】**********************************************");

 

 

所謂的非同步回調,就是在非同步線程執行完在執行的代碼塊。

2.非同步等待(阻塞):

 

主線程等待子線程有這麼幾種方式:

1.主線程等待子線程的時候有返回,比如說我們常見的進度條功能,執行一點我就返回下。2.主線程等待子線程有時間限制,例如:中午放學我等你五分鐘,你要是不完事,我就先吃飯去了。3.主線程等待子線程無返回,比如死等,今天的代碼我學不會了,我就不睡覺了。下麵我們分別看看這三種情況。我們管操作線程等待的過程叫做阻塞(se)進程.阻塞主進程以後等待子線程的執行,我們成為線程的阻塞,

剛纔我們是使用回調,在非同步執行完成,才執行了一個代碼塊,這個時候messagebax 已經輸出了,現在我們開看看課堂下的學生表現。“將下列代碼放到我們  MessageBox.Show("今天想玩,怎麼騙過老師呢,下課玩點什麼呢!!書還在繼續讀,但是我的心已經飛了!!!");”之後,我們來看看

 while (!iAsyncResult.IsCompleted)//邊等待邊操作,可以用於做進度條
            {
                Thread.Sleep(100);//建議控制100毫秒一次
                Console.WriteLine("老師還在教我們讀書.....請等待...............");
             
            }
            //當非同步完成以後,我們在執行下邊的這句話
            Console.WriteLine("學生甲:沖啊..............打籃球全");
            Console.WriteLine("學生乙:王美美.......我愛你!咱們交往吧....*#*#*#**??!");
            Console.WriteLine("學生丙:呼呼呼呼呼呼呼呼。。。。。嚕。。。。。。。。。。今天的肉丸子真好吃,真希望這不是夢啊");
            Console.WriteLine("學生丁:大海啊,就像媽媽一樣,海浪啊!你為啥這麼猛!總是在我人生巔峰......被打斷");
            Console.WriteLine("學生丙:別BiBi了,海浪是你後媽,滾一邊去淫詩去!別TMD打擾老子睡覺");

 

剛纔執行的線程等待在阻塞的過程中是有損耗的,我們損耗 的是時間,所以回調會在子線程之前執行,那麼我們想要無損耗怎麼去寫,怎麼去阻塞我們的主線程呢 “ bool RunBool = iAsyncResult.AsyncWaitHandle.WaitOne();”; 當子線程執行成功了,就會返回TRUE,當子線程執行過程中出現exection 以後,就返回false;

這種寫法主線程就無法返回了。但是我們可以新建立一個線程去監控子線程。這裡就不寫那麼複雜了。

第二種情況,我只等你兩秒鐘,有時間限制的阻塞

 

   #region 非同步等待1 有損耗 帶返回
            //while (!iAsyncResult.IsCompleted)//邊等待邊操作,可以用於做進度條
            //{
            //    Thread.Sleep(100);//建議控制100毫秒一次
            //    Console.WriteLine("老師還在教我們讀書.....請等待...............");

            //}
            #endregion
            #region 非同步等待2 無損耗 無返回
            //bool RunBool = iAsyncResult.AsyncWaitHandle.WaitOne();//返回結果是子線程執行成功或者失敗,不是實時返回的。
            //iAsyncResult.AsyncWaitHandle.WaitOne(-1);//寫法2 
            #endregion
            #region 有時間限制的非同步等待
            iAsyncResult.AsyncWaitHandle.WaitOne(2000);//我最多等你2秒鐘,如果你提前完事,我們提前走
            #endregion 
            //當非同步完成以後,我們在執行下邊的這句話
            Console.WriteLine("學生甲:沖啊..............打籃球全");
            Console.WriteLine("學生乙:王美美.......我愛你!咱們交往吧....*#*#*#**??!");
            Console.WriteLine("學生丙:呼呼呼呼呼呼呼呼。。。。。嚕。。。。。。。。。。今天的肉丸子真好吃,真希望這不是夢啊");
            Console.WriteLine("學生丁:大海啊,就像媽媽一樣,海浪啊!你為啥這麼猛!總是在我人生巔峰......被打斷");
            Console.WriteLine("學生丙:別BiBi了,海浪是你後媽,滾一邊去淫詩去!別TMD打擾老子睡覺");
            Console.WriteLine("**********************btnSync_Click End 【" + Thread.CurrentThread.ManagedThreadId + "】**********************************************");

 

 

這種子線程執行兩秒以後,主線程在執行這個問題經常會在面試裡面問。面試經常會問,主線程A 至少要執行10,秒,子線程B至少要執行30秒,如何讓主線程在子線程執行20秒開始執行。

下邊我們就舉例,代碼不會,我就要學習了學習不會就不睡覺,就死學到底了。

    #region 非同步等待死等
            //死等就是,只要你不異常,就必須給我一個結果,比如學習,必須學會為止
            action.EndInvoke(iAsyncResult);//EndInvoke 的返回值取決與你的委托!你的委托有返回值,我就有返回值。
            #endregion 

 

 

註意圖上反應的問題。其實回調執行的是子線程。我們死等(阻塞 主線程等待子線程)的是子線程,而不是子線程的回調。這個時候是主線程和子線程一起執行的(線程的無序)。這就會照成CPU 更大的波峰,很容易宕機。由於演示這種結果不容易,需要執行很多遍,這裡沒有截取到CPU 波峰。本人I7 CPU 基本都趕到頂了。

通過上圖可以看出,主線程和子線程的執行先後順序不一定誰先後,線程是無序的。

如果下了本文demo 的同學會發現,這個時候UI 是卡住的,主窗體UI阻塞,所以窗體是無法移動的。

。到這裡非同步我們就學習完了,下邊總結下

 

四、總結

 1.非同步等待和非同步回調的區別?面試會考的哦!!

答:非同步等待是在子線程執行完成以後回到主線程,j解除主線程的阻塞繼續執行,而非同步回調是子線程執行完成以後在去以子線程再去執行的任務代碼塊。

非同步等待卡主線程,回調不卡主線程。

委托中回調不可以取得子線程執行的結果,等待可以通過線程狀態參數取得執行結果。

2.主線程A 需要執行1秒,而子線程B需要執行3秒。如果讓B執行2秒以後在執行?或者 介面A 調用5秒沒結果,我就調用介面B去取數據?在介面B取到數據以後,介面如果也取到數據,仍然使用結果B的,怎麼去做。

答:使用 iAsyncResult.AsyncWaitHandle.WaitOne(2000);

 關於介面(WebApi ,Service)的情況,我們也是需要使用線程等待,但是這個時候我們就要加鎖或者加計時器 StopWatch 去做。關於鎖以後在談。但是加鎖會影響效率,計時器在多服務情況下還不准確,這是大多數面試者的回答。

我們把沒有演示的一點點知識在這裡演示下。

 

我們一直沒有說這個參數有什麼做用,這裡簡單介紹下。當我線程啟動的時候,我可以啟動多條線程,但是我無法確定那個線程執行的過程,這個時候我們可以通過這個參數傳遞線程狀態。這裡不過多解釋。有用到的私聊本人。

3.如果我想使用子線程的結果去做主線程的參數,如何去做。請說明你的理由。這裡不過多解釋了,案列很清晰。

4.這裡的阻塞是卡主線程的,我們如何不卡主線程??

下節多線程中找答案。

 

個人總結:

1.net 非同步支持

Net framework可以讓你非同步調用任何方法。為達這樣的目的,你可以定義一個與你要調用的方法的簽名相同的委托。公共語言運行時將自動為該委托定義與簽名相同的BeginInvok和EndInvoke方法。

非同步委托調用BeginInvok和EndInvoke方法,但在.NET Compact Framework中並不支持。

 

.NET Framework 允許您非同步調用任何方法。定義與您需要調用的方法具有相同簽名的委托;公共語言運行庫將自動為該委托定義具有適當簽名

的 BeginInvoke 和 EndInvoke 方法。

BeginInvoke 方法用於啟動非同步調用。它與您需要非同步執行的方法具有相同的參數,只不過還有兩個額外的參數(將在稍後描述)。

BeginInvoke 立即返回,不等待非同步調用完成。
BeginInvoke 返回 IasyncResult,可用於監視調用進度。

EndInvoke 方法用於檢索非同步調用結果。調用 BeginInvoke 後可隨時調用 EndInvoke 方法;如果非同步調用未完成,EndInvoke 將一直阻塞到

非同步調用完成。EndInvoke 的參數包括您需要非同步執行的方法的 out 和 ref 參數(在 Visual Basic 中為 <Out> ByRef 和 ByRef)以及由

BeginInvoke 返回的 IAsyncResult。

四種使用 BeginInvoke 和 EndInvoke 進行非同步調用的常用方法。調用了 BeginInvoke 後,可以:

1.進行某些操作,然後調用 EndInvoke 一直阻塞到調用完成。
2.使用 IAsyncResult.AsyncWaitHandle 獲取 WaitHandle,使用它的 WaitOne 方法將執行一直阻塞到發出 WaitHandle 信號,然後調用

EndInvoke。這裡主要是主程式等待非同步方法,等待非同步方法的結果。
3.輪詢由 BeginInvoke 返回的 IAsyncResult,IAsyncResult.IsCompeted確定非同步調用何時完成,然後調用 EndInvoke。此處理個人認為與
相同。
4.將用於回調方法的委托傳遞給 BeginInvoke。該方法在非同步調用完成後在 ThreadPool 線程上執行,它可以調用 EndInvoke。這是在強制裝

換回調函數裡面IAsyncResult.AsyncState(BeginInvoke方法的最後一個參數)成委托,然後用委托執行EndInvoke。
警告   始終在非同步調用完成後調用 EndInvoke。

通過EndInvoke方法檢測非同步調用的結果。如果非同步調用尚未完成,EndInvoke將阻塞調用線程,直到它完成。EndInvoke參數包括out和ref參數,本文沒有講到,另外本文沒有演示EndInvoke 返回值 。

2.同步方法和非同步方法的區別

同步方法調用在程式繼續執行之前需要等待同步方法執行完畢返回結果
非同步方法則在被調用之後立即返回以便程式在被調用方法完成其任務的同時執行其它操作

 Demo 下載

 88.睡覺

 


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

-Advertisement-
Play Games
更多相關文章
  • 1.使用此分析器需要輸入兩個量: String str1=運算符號有前後有空格的數學表達式(如 1 + 2 * ( 3+1) - 5 #),併在最後添加‘#’字元作為結束標誌; String str2=無空格的分割的數學表達式(如 1+2*(3+1)-5#),同樣添加‘#’字元作為結束標誌; 2.註 ...
  • 本節用類與靜態變數改造之前的例子:php根據命令行參數生成配置文件 ghostinit.php: ghost: 執行結果: callstatic繼續改造: ghostinit.php: ghost: 把配置獨立成一個類 ghostconfig.php: 把這兩個屬性註釋,也可以正常運行, php允許 ...
  • 對話框介紹 對話框是於用戶進行簡易交互的頂層視窗 QDialog是Qt中所有對話框視窗的父類,是一種容器類型的組件 QDialog繼承於QWidget類,如下圖所示: QWidget和QDialog有什麼不同 QDialog: QDialog是定製了視窗式樣的特殊的QWidget QDialog只能 ...
  • 6-1 人 :使用一個字典來存儲一個熟人的信息,包括名、姓、年齡和居住的城市。該字典應包含鍵first_name 、last_name 、age 和city 。將存儲在該字典中 的每項信息都列印出來。 輸出: 6-2 喜歡的數字 :使用一個字典來存儲一些人喜歡的數字。請想出5個人的名字,並將這些名字 ...
  • C# 實現表單的自動化測試<通過程式控制一個網頁> ...
  • AspNetCoreApi 跨域處理 如果咱們有處理過MV5 跨域問題這個問題也不大。 (1)為什麼會出現跨域問題: 瀏覽器安全限制了前端腳本跨站點的訪問資源,所以在調用WebApi 介面時不能成功訪問資源,原因“同源策略”的存在: 同源指以下幾點相同 (1) IP地址/功能變數名稱 (2) 埠號 (3) ...
  • 最近因為公司業務需要,又有機會擼winform了,這次的需求是因為公司有項目申報的這塊業務,項目申報前期需要關註政府發佈的相關動態信息,政府部門網站過多,人工需要一個一個網站去瀏覽和查閱,有時候還會遺漏掉,因此呢,我們打算用爬蟲+移動端web來做,我呢主要負責爬蟲和web Api。 爬蟲篇 爬蟲主要 ...
  • 在WPF里用MediaElement控制項,實現一個迴圈播放單一視頻的程式,同時可以控制視頻的播放、暫停、停止。 一種方式,使用MediaElement.MediaEnded事件,在視頻播放結束後,自動重新播放; 另一種方式,使用WPF定時器,在定時器事件里寫入視頻播放代碼。 後者優點是可以控制迴圈時 ...
一周排行
    -Advertisement-
    Play Games
  • 1. 說明 /* Performs operations on System.String instances that contain file or directory path information. These operations are performed in a cross-pla ...
  • 視頻地址:【WebApi+Vue3從0到1搭建《許可權管理系統》系列視頻:搭建JWT系統鑒權-嗶哩嗶哩】 https://b23.tv/R6cOcDO qq群:801913255 一、在appsettings.json中設置鑒權屬性 /*jwt鑒權*/ "JwtSetting": { "Issuer" ...
  • 引言 集成測試可在包含應用支持基礎結構(如資料庫、文件系統和網路)的級別上確保應用組件功能正常。 ASP.NET Core 通過將單元測試框架與測試 Web 主機和記憶體中測試伺服器結合使用來支持集成測試。 簡介 集成測試與單元測試相比,能夠在更廣泛的級別上評估應用的組件,確認多個組件一起工作以生成預 ...
  • 在.NET Emit編程中,我們探討了運算操作指令的重要性和應用。這些指令包括各種數學運算、位操作和比較操作,能夠在動態生成的代碼中實現對數據的處理和操作。通過這些指令,開發人員可以靈活地進行算術運算、邏輯運算和比較操作,從而實現各種複雜的演算法和邏輯......本篇之後,將進入第七部分:實戰項目 ...
  • 前言 多表頭表格是一個常見的業務需求,然而WPF中卻沒有預設實現這個功能,得益於WPF強大的控制項模板設計,我們可以通過修改控制項模板的方式自己實現它。 一、需求分析 下圖為一個典型的統計表格,統計1-12月的數據。 此時我們有一個需求,需要將月份按季度劃分,以便能夠直觀地看到季度統計數據,以下為該需求 ...
  • 如何將 ASP.NET Core MVC 項目的視圖分離到另一個項目 在當下這個年代 SPA 已是主流,人們早已忘記了 MVC 以及 Razor 的故事。但是在某些場景下 SSR 還是有意想不到效果。比如某些靜態頁面,比如追求首屏載入速度的時候。最近在項目中回歸傳統效果還是不錯。 有的時候我們希望將 ...
  • System.AggregateException: 發生一個或多個錯誤。 > Microsoft.WebTools.Shared.Exceptions.WebToolsException: 生成失敗。檢查輸出視窗瞭解更多詳細信息。 內部異常堆棧跟蹤的結尾 > (內部異常 #0) Microsoft ...
  • 引言 在上一章節我們實戰了在Asp.Net Core中的項目實戰,這一章節講解一下如何測試Asp.Net Core的中間件。 TestServer 還記得我們在集成測試中提供的TestServer嗎? TestServer 是由 Microsoft.AspNetCore.TestHost 包提供的。 ...
  • 在發現結果為真的WHEN子句時,CASE表達式的真假值判斷會終止,剩餘的WHEN子句會被忽略: CASE WHEN col_1 IN ('a', 'b') THEN '第一' WHEN col_1 IN ('a') THEN '第二' ELSE '其他' END 註意: 統一各分支返回的數據類型. ...
  • 在C#編程世界中,語法的精妙之處往往體現在那些看似微小卻極具影響力的符號與結構之中。其中,“_ =” 這一組合突然出現還真不知道什麼意思。本文將深入剖析“_ =” 的含義、工作原理及其在實際編程中的廣泛應用,揭示其作為C#語法奇兵的重要角色。 一、下劃線 _:神秘的棄元符號 下劃線 _ 在C#中並非 ...