多線程(線程同步2)

来源:http://www.cnblogs.com/yonghuacui/archive/2016/12/22/6208381.html
-Advertisement-
Play Games

在上一篇多線程(線程同步1)中,我們主要學習了執行基本的原子操作、使用Mutex構造以及SemaphoreSlim構造,在這一篇中我們主要學習如何使用AutoResetEvent構造、ManualResetEventSlim構造和CountDownEvent構造。 四、使用AutoResetEven ...


  在上一篇多線程(線程同步1)中,我們主要學習了執行基本的原子操作、使用Mutex構造以及SemaphoreSlim構造,在這一篇中我們主要學習如何使用AutoResetEvent構造、ManualResetEventSlim構造和CountDownEvent構造。

四、使用AutoResetEvent構造

  在這一小節中,我們將學習如何使用AutoResetEvent構造從一個線程向另一個線程發送通知。AutoResetEvent通知一個等待線程某個事件已經發生。具體步驟如下所示:

1、使用Visual Studio 2015創建一個新的控制台應用程式。

2、雙擊打開“Program.cs”文件,編寫代碼如下所示:

 1 using System;
 2 using System.Threading;
 3 using static System.Console;
 4 using static System.Threading.Thread;
 5 
 6 namespace Recipe04
 7 {
 8     class Program
 9     {
10         private static AutoResetEvent workerEvent = new AutoResetEvent(false);
11         private static AutoResetEvent mainEvent = new AutoResetEvent(false);
12 
13         static void Process(int seconds)
14         {
15             WriteLine("Starting a long running work...");
16             Sleep(TimeSpan.FromSeconds(seconds));
17             WriteLine("Work is done!");
18             workerEvent.Set();
19             WriteLine("Waiting for a main thread to complete its work");
20             mainEvent.WaitOne();
21             WriteLine("Starting second operation...");
22             Sleep(TimeSpan.FromSeconds(seconds));
23             WriteLine("Work is done!");
24             workerEvent.Set();
25         }
26 
27         static void Main(string[] args)
28         {
29             var t = new Thread(() => Process(10));
30             t.Start();
31 
32             WriteLine("Waiting for another thread to complete work");
33             workerEvent.WaitOne();
34             WriteLine("First operation is completed!");
35             WriteLine("Performing an operation on a main thread");
36             Sleep(TimeSpan.FromSeconds(5));
37             mainEvent.Set();
38             WriteLine("Now running the second operation on a second thread");
39             workerEvent.WaitOne();
40             WriteLine("Second operation is completed!");
41         }
42     }
43 }

3、運行該控制台應用程式,運行效果(每次運行效果可能不同)如下圖所示:

  在第10~11行代碼處,我們定義了兩個AutoResetEvent實例:workerEvent和mainEvent。workerEvent用於從新建線程中向主線程發送通知,mainEvent用於從主線程向新建線程發送通知。在調用AutoResetEvent的構造方法的時候,我們給該構造方法的“initialState”參數傳遞了false值,指定AutoResetEvent實例的初始狀態為“無信號狀態”,這意味著調用AutoResetEvent實例的“WaitOne”方法的線程將會被阻塞,直到我們調用AutoResetEvent實例的“Set”方法之後,該線程才會繼續執行。如果我們將AutoResetEvent類的構造方法的“initialState”參數值設置為true,則AutoResetEvent實例的初始狀態為“信號狀態”,那麼第一個調用AutoResetEvent實例的“WaitOne”方法的線程將會被立即執行,然後AutoResetEvent實例的狀態自動變為“無信號狀態”,這個時候,當我們再次調用AutoResetEvent的“WaitOne”方法後,必須在另一個線程中調用AutoResetEvent的“Set”方法才能繼續執行當前的線程。

  在第29行代碼處,我們創建了一個新的線程用於執行“Process”方法,併在第30行代碼處啟動線程。

  在第33行代碼處,我們調用AutoResetEvent實例workerEvent的“WaitOne”方法,導致主線程被阻塞,然而在我們在第29行代碼處創建的線程中,我們調用了AutoResetEvent實例WorkerEvent的“Set”方法,因此,主線程得以繼續執行。當執行到第20行代碼處,我們在新建線程中調用了AutoResetEvent實例mainEvent的“WaitOne”方法,因此導致新建線程被阻塞,然而在主線程執行到第37行代碼處,我們調用了AutoResetEvent實例mainEvent的“Set”方法,因此,新建線程得以繼續執行。而主線程在執行到第39行代碼處,主線程又被阻塞,而新建線程執行到第24行代碼處,導致主線程得以繼續執行,因此,主線程執行到第40行代碼,控制台應用程式正常結束。

  AutoResetEvent是kernel-time構造,因此,如果沒有必要,我們建議使用下一節介紹的ManualResetEventslim來替代AutoResetEvent。

五、使用ManualResetEventSlim構造

  在這一小節中,我們將學習如何使用ManualResetEventSlim構造在多個線程之間更加靈活地發送通知。具體步驟如下所示:

1、使用Visual Studio 2015創建一個新的控制台應用程式。

2、雙擊打開“Program.cs”文件,編寫代碼如下所示:

 1 using System;
 2 using System.Threading;
 3 using static System.Console;
 4 using static System.Threading.Thread;
 5 
 6 namespace Recipe05
 7 {
 8     class Program
 9     {
10         private static ManualResetEventSlim mainEvent = new ManualResetEventSlim(false);
11 
12         static void TravelThroughGates(string threadName, int seconds)
13         {
14             WriteLine($"{threadName} falls to sleep");
15             Sleep(TimeSpan.FromSeconds(seconds));
16             WriteLine($"{threadName} waits for the gates to open!");
17             mainEvent.Wait();
18             WriteLine($"{threadName} enters the gates!");
19         }
20         
21         static void Main(string[] args)
22         {
23             var t1 = new Thread(() => TravelThroughGates("Thread 1", 5));
24             var t2 = new Thread(() => TravelThroughGates("Thread 2", 6));
25             var t3 = new Thread(() => TravelThroughGates("Thread 3", 12));
26 
27             t1.Start();
28             t2.Start();
29             t3.Start();
30 
31             Sleep(TimeSpan.FromSeconds(6));
32             WriteLine("The gates are now open!");
33             mainEvent.Set();
34             Sleep(TimeSpan.FromSeconds(2));
35             mainEvent.Reset();
36             WriteLine("The gates have been closed!");
37             Sleep(TimeSpan.FromSeconds(10));
38             WriteLine("The gates are now open for the second time!");
39             mainEvent.Set();
40             Sleep(TimeSpan.FromSeconds(2));
41             WriteLine("The gates have been closed!");
42             mainEvent.Reset();
43         }
44     }
45 }

3、運行該控制台應用程式,運行效果(每次運行效果可能不同)如下圖所示:

  在第10行代碼處,我們定義了一個ManualResetEventSlim類型的實例mainEvent,並給它的構造方法的“initialState”參數傳遞了false值,表示該對象的初始狀態為“無信號狀態”。

  在第23~25行代碼處,我們創建了三個線程t1、t2和t3。這三個線程都用於執行“TraveThroughGates”方法,在該方法的內部,我們調用了ManualResetEventSlim實例mainEvent的“Wait”方法,以阻塞t1、t2和t3線程的執行。

  在第31行代碼處,我們讓主線程阻塞6秒鐘,在這六秒鐘內,線程t1和t2都執行到第17行代碼處,這個時候線程t1和t2都阻塞,並且等待mainEvent的“Set”方法被調用,以接收信號後繼續執行。主線程阻塞6秒鐘後,會執行第33行代碼,執行完畢這行代碼之後,線程t1和t2都會接收到通知,因此,線程t1和t2都會繼續往下執行,從而都執行第18行代碼,之後線程t1和t2執行完畢,結束。

  由於線程t3在主線程執行到第33行代碼處的時候,還在阻塞(因為執行了第15行代碼)中,因此線程t3在主線程執行到第33行代碼處的時候不受影響,繼續阻塞。

  當主線程執行到第34行代碼處的時候,線程t3依然在阻塞狀態中。在主線程執行了第35行代碼之後,mainEvent被重置為“無信號狀態”。當主線程執行到第37行代碼處,主線程被阻塞10秒鐘。在主線程被阻塞的10秒鐘內,線程t3會執行到第17行代碼處,從而t3線程被阻塞,等待通知的到來,才能繼續執行。

  當主線程阻塞10秒鐘之後,會執行第39行代碼,從而導致線程t3繼續執行,因此會執行第18行代碼,線程t3結束。

  然後主線程阻塞2秒鐘後,又將mainEvent重置為“無信號狀態”,然後主線程結束。

六、使用CountdownEvent構造

  在這一小節中,我們將學習如何使用CountdownEvent構造等待發送一定數量的通知後,才繼續執行被阻塞的線程。學習步驟如下所示:

1、使用Visual Studio 2015創建一個新的控制台應用程式。

2、雙擊打開“Program.cs”文件,編寫代碼如下所示:

 1 using System;
 2 using System.Threading;
 3 using static System.Console;
 4 using static System.Threading.Thread;
 5 
 6 namespace Recipe06
 7 {
 8     class Program
 9     {
10         private static CountdownEvent countdown = new CountdownEvent(2);
11 
12         static void PerformOperation(string message, int seconds)
13         {
14             Sleep(TimeSpan.FromSeconds(seconds));
15             WriteLine(message);
16             countdown.Signal();
17         }
18 
19         static void Main(string[] args)
20         {
21             WriteLine("Starting two operations");
22             var t1 = new Thread(() => PerformOperation("Operation 1 is completed", 4));
23             var t2 = new Thread(() => PerformOperation("Operation 2 is completed", 8));
24 
25             t1.Start();
26             t2.Start();
27             countdown.Wait();
28             WriteLine("Both operations have been completed.");
29             countdown.Dispose();
30         }
31     }
32 }

3、運行該控制台應用程式,運行效果如下圖所示:

  在第10行代碼處,我們創建了一個CountdownEvent的實例countdown,並給該構造方法的“initialCount”參數傳遞了數值2,表示我們希望等待2個通知發送完畢後,被阻塞的線程才能繼續執行。

  在第22~23行代碼處,我們創建了兩個新線程用於執行“PerformOperation”方法,在該方法中,我們調用了countdown的“Signal”方法,用於發送通知,並減小CountdownEvent的CurrentCount的值,當CurrentCount的值減少到0時,被阻塞的線程才能繼續執行。

  在第27行代碼處,我們在主線程中調用了countdown的“Wait”方法,從而主線程被阻塞,直到接收到通知並且CurrentCount的值為0時,主線程才能繼續執行。

  註意,如果將第10行代碼處的2修改為3,再次運行該程式,主線程會一直等待,不會結束,因為CurrentCount的值沒有減少到0。


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

-Advertisement-
Play Games
更多相關文章
  • 靜態用戶名和密碼的登錄練習 private void button2_Click(object sender, EventArgs e) { textUser.Text = Convert.ToString(textUser.Text); textBox2.Text = textBox2.Text. ...
  • 數組、集合、異常捕獲 數組: 一次性存儲多個相同類型的變數。 一維數組: 語法: 數組類型[] 數組名=new 數組類型[數組長度]; 聲明數組的語法: A.數據類型 [] 數組名稱= new 數據類型[2]{1,2}; B.數據類型 [] 數組名稱 = new 數據類型[數組大小]; C. 數據類 ...
  • 不久前,我發佈了一個調試工具:發佈:.NET開發人員必備的可視化調試工具(你值的擁有)不久前,我發佈了一個調試工具:發佈:.NET開發人員必備的可視化調試工具(你值的擁有) 效果是這樣的:之後,有小部分用戶反映,工具不生效~~~然後,建議小部分用戶換個電腦環境試試,就好了~~~於是... ...
  • 在ASP.NET MVC程式中,需要給一個radio list表綁定一個值。下麵是Insus.NET實現的方法: 使用foreach來迴圈radio每一個選項,如果值與選項的值相同,那這個選項為選中,反之為未選。 如果覺得html有點多,可以稍修改如下: 使用 bool ? object : obj ...
  • 一、演示: 介面查看:http://apidoc.docode.top/ 介面後臺:http://apiadmin.docode.top/ 登錄:administrator,123456 二、使用到的技術: ①、AdminLite(基於Bootstrap的響應式UI模板) ②、ASP.NET MVC ...
  • 支持Oracle、MSSQL、MySQL、SQLite四種資料庫,支持事務,支持對象關係映射;已在多個項目中實際使用。 沒有語法糖,學習成本幾乎為0,拿來即用。 DBHelper類完整代碼: using System; using System.Collections.Generic; using ...
  • 效果圖 思路 拿到父級窗體的內容,放入一個容器里,再在容器里放入一個半透明層.將整個容器賦給父級窗體的內容. 關閉時反向操作. 代碼 消息窗彈出時 消息框關閉時 源碼下載:MessageBoxWithLayer.zip ...
  • 上一篇文章學習了IL的入門,接下來我們再通過兩個例子來瞭解下類的屬性、構造函數以及介面的使用 一、類的屬性、構造函數 1、先看下我們要構建的類的C#代碼,然後再進行IL的實現,示例代碼如下: [Serializable] public class Dynamic { public int _a = ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...