併發編程 --- 信號量線程同步

来源:https://www.cnblogs.com/pandefu/archive/2023/07/18/17536279.html
-Advertisement-
Play Games

## 引言 上文[編碼技巧 同步鎖對象的選定](url)中,提到了在C#中,讓線程同步有兩種方式: - 鎖(lock、Monitor等) - 信號量(EventWaitHandle、Semaphore、Mutex) 加鎖是最常用的線程同步的方法,就不再討論,本篇主要討論使用信號量同步線程。 ## W ...


引言

上文編碼技巧 --- 同步鎖對象的選定中,提到了在C#中,讓線程同步有兩種方式:

  • 鎖(lock、Monitor等)
  • 信號量(EventWaitHandle、Semaphore、Mutex)

加鎖是最常用的線程同步的方法,就不再討論,本篇主要討論使用信號量同步線程。

WaitHandle介紹

實際上,再C#中 EventWaitHandleSemaphoreMutex 都是抽象類 WaitHandle 的派生類,它提供了一組等待信號的方法和屬性。如下圖:

image.png

主要包含靜態方法 SignalAndWait()WaitAll()WaitAny()及一個虛方法WaitOne()。下麵介紹一個這幾個方法。

介紹這些方法之前,先簡單介紹一下 WaitHandle 的派生類 EventWaitHandle,該派生類有兩個實現類 AutoResetEventManualResetEvent,其方法列表如下:
image.png

重點說一下,Set()Reset():

  • Set()方法設置事件為有信號狀態:當調用 Set() 時,它將被設置為終止狀態,並允許一個或多個等待該事件的線程繼續執行。
  • Reset()方法設置事件為無信號狀態:當調用 Reset() 時,它將被設置為非終止狀態,所有想要等待該事件的線程都將被阻塞,直到調用 Set() 方法使其變為終止狀態。

註意:這裡的有信號,無信號的意思類似於紅綠燈,有信號你才能夠通行,對於線程來說,有信號意味著可以接著往下運行,無信號則阻塞等待信號。

接下來的代碼段演示皆使用 AutoResetEvent 進行演示。

SignalAndWait()

當調用 WaitHandle 的靜態方法 SignalAndWait() 時,會使得當前線程等待一個 WaitHandle 對象的信號,同時設置另一個 WaitHandle 對象為有信號狀態。當第一個 WaitHandle 對象收到信號時,當前線程繼續執行,同時第二個 WaitHandle 對象變為無信號狀態。

static AutoResetEvent event1 = new AutoResetEvent(false);
static AutoResetEvent event2 = new AutoResetEvent(false);

static void Main(string[] args)
{
    Thread t1 = new Thread(new ThreadStart(Worker1));
    Thread t2 = new Thread(new ThreadStart(Worker2));

    t1.Start();
    t2.Start();

    Console.ReadLine();
}

static void Worker1()
{
    Console.WriteLine("線程1開始執行……");

    event1.WaitOne(); // 等待事件1的發生

    Console.WriteLine("線程1收到事件1的信號,繼續執行……");

    WaitHandle.SignalAndWait(event1, event2); // 發送事件2的信號並等待事件2的發生

    Console.WriteLine("線程1收到事件2的信號,繼續執行……");
}

static void Worker2()
{
    Console.WriteLine("線程2開始執行……");

    Thread.Sleep(2000); // 模擬線程2的執行時間

    Console.WriteLine("線程2發出事件1的信號……");

    event1.Set(); // 發送事件1的信號

    Thread.Sleep(2000); // 模擬線程2的執行時間

    Console.WriteLine("線程2發出事件2的信號……");

    WaitHandle.SignalAndWait(event2, event1); // 發送事件1的信號並等待事件1的發生

    Console.WriteLine("線程2收到事件1的信號,繼續執行……");
}

輸出:

線程1開始執行……
線程2開始執行……
線程2發出事件1的信號……
線程1收到事件1的信號,繼續執行……
線程2發出事件2的信號……
線程2收到事件1的信號,繼續執行……
線程1收到事件2的信號,繼續執行……

WaitAll()

當調用 WaitHandle 的靜態方法 WaitAll() 時,它可以等待多個WaitHandle對象的信號,直到所有對象都收到信號或等待超時。

static AutoResetEvent[] events = new AutoResetEvent[3]
{
    new AutoResetEvent(false),
    new AutoResetEvent(false),
    new AutoResetEvent(false)
};

static void Main(string[] args)
{
    Thread t1 = new Thread(new ThreadStart(Worker1));
    Thread t2 = new Thread(new ThreadStart(Worker2));

    t1.Start();
    t2.Start();

    Console.ReadLine();
}

static void Worker1()
{
    Console.WriteLine("線程1開始執行……");

    WaitHandle.WaitAll(events); // 等待所有事件的發生

    Console.WriteLine("線程1收到所有事件的信號,繼續執行……");
}

static void Worker2()
{
    Console.WriteLine("線程2開始執行……");

    Thread.Sleep(2000); // 模擬線程2的執行時間

    Console.WriteLine("線程2發出事件1的信號……");

    events[0].Set(); // 發送事件1的信號

    Thread.Sleep(2000); // 模擬線程2的執行時間

    Console.WriteLine("線程2發出事件2的信號……");

    events[1].Set(); // 發送事件2的信號

    Thread.Sleep(2000); // 模擬線程2的執行時間

    Console.WriteLine("線程2發出事件3的信號……");

    events[2].Set(); // 發送事件3的信號
}

輸出:

線程1開始執行……
線程2開始執行……
線程2發出事件1的信號……
線程2發出事件2的信號……
線程2發出事件3的信號……
線程1收到所有事件的信號,繼續執行……

WaitAny()

當調用 WaitHandle 的靜態方法 WaitAny() 時,它可以等待多個WaitHandle對象中的任意一個對象收到信號,直到有一個對象收到信號或等待超時。

static AutoResetEvent[] events = new AutoResetEvent[3]
{
    new AutoResetEvent(false),
    new AutoResetEvent(false),
    new AutoResetEvent(false)
};

static void Main(string[] args)
{
    Thread t1 = new Thread(new ThreadStart(Worker1));
    Thread t2 = new Thread(new ThreadStart(Worker2));

    t1.Start();
    t2.Start();

    Console.ReadLine();
}

static void Worker1()
{
    Console.WriteLine("線程1開始執行……");

    WaitHandle.WaitAny(events); // 等待任意事件的發生

    Console.WriteLine("線程1收到任意事件的信號,繼續執行……");
}

static void Worker2()
{
    Console.WriteLine("線程2開始執行……");

    var randomIndex = new Random().Next(0, 2);

    Console.WriteLine("線程2發出任意一個事件的信號……");

    events[randomIndex].Set(); //發送任意一個事件的信號
}

輸出:

線程1開始執行……
線程2開始執行……
線程2發出任意一個事件的信號……
線程1收到任意事件的信號,繼續執行……

WaitOne()

WaitOne()方法上文中其實已經用到了,它就表示阻塞當前線程,等待當前 WaitHandle 對象收到信號,直到對象收到信號或等待超時。如果WaitHandle對象收到信號,WaitOne()方法返回true,否則返回false。使用簡單就不在貼代碼段。

派生類的異同

上面已經提到了EventWaitHandleSemaphoreMutex 都是抽象類 WaitHandle 的派生類,它們的作用類似,但在使用和實現上有一些不同。下麵我們來簡單介紹下它們的異同點。

  1. EventWaitHandle:

    EventWaitHandle 有兩種類型:AutoResetEventManualResetEvent。它們的區別在於AutoResetEvent 在有信號時只通知一個等待線程,而 ManualResetEvent 在有信號時通知所有等待線程。
    兩者設置為終止狀態的方式都是調用 Set() 方法。

  2. Semaphore

    Semaphore 可以用於多個線程之間的資源控制。Semaphore 可以控制同時訪問共用資源的線程數量。設置為終止狀態的方式是調用 Release() 方法。

  3. Mutex

    Mutex 可以用於多個線程之間的互斥訪問共用資源。Mutex 可以保證同一時間只有一個線程可以訪問共用資源。設置為終止狀態的方式是調用 ReleaseMutex() 方法。

作者: Niuery Daily

出處: https://www.cnblogs.com/pandefu/>

郵箱: [email protected]

關於作者:.Net Framework,.Net Core ,WindowsForm,WPF ,控制項庫,多線程

本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出 原文鏈接,否則保留追究法律責任的權利。 如有問題, 可郵件咨詢。


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

-Advertisement-
Play Games
更多相關文章
  • 有時候,我們在處理大量文檔的時候,需要批量給Word文檔添加印章處理,方便列印操作,本篇隨筆介紹利用Aspose.Word對Word文件添加印章處理以及實現業務數據的替換處理。 ...
  • 在編程方面,從來都是實踐出真知,書讀百遍其義自見,所以實戰是最好的提升自己編程能力的方式。 前一段時間,寫了一些實戰系列文章,如: ASP.NET MVC開發學生信息管理系統 Vue+Antdv+Asp.net WebApi開發學生信息管理系統 WPF+Prism+MAH+Asp.net Web A ...
  • 這篇文章主要介紹了介面的概念、定義和實現,以及顯式/隱式實現介面的場景。文章還對介面和抽象類進行了比較,指出它們的區別。同時,文章提供了詳細的代碼示例,方便讀者理解和實踐。 ...
  • ## 引言 眾所周知,使用線程可以極大的提高應用程式的效率和響應性,提高用戶體驗,但是不可以無節制的使用線程,為什麼呢? ## 線程的開銷 線程的開銷實際上是非常大的,我們從空間開銷和時間開銷上分別討論。 ### 線程的空間開銷 線程的空間開銷來自這四個部分: 1. 線程內核對象(Thread Ke ...
  • 在一些文檔處理中,我們需要對PDF蓋上公司的印章操作,本篇隨筆介紹利用Spire.Pdf實現PDF添加印章的操作,如全章和騎縫章的處理。 ...
  • 模型配置可以通過Fluent API和註解的方式 FluentAPI步驟 新建Products 和Category類 新建Products類 Products public class Product { public int Id { get; set; } public string Name ...
  • ## 一:背景 ### 1. 講故事 如果要分析 Linux上的 .NET程式 CPU 爆高,按以往的個性我肯定是抓個 dump 下來做事後分析,這種分析模式雖然不重但也不輕,還需要一定的底層知識,那有沒有傻瓜式的 CPU 爆高分析方式呢? 相信有很多朋友知道 **B站713事件**,最終就是用 p ...
  • > 註:本文隸屬於《理解ASP.NET Core》系列文章,請查看置頂博客或[點擊此處查看全文目錄](https://www.cnblogs.com/xiaoxiaotank/p/15185288.html) # 概述 在微服務化的架構設計中,網關扮演著重要的看門人角色,它所提供的功能之一就是**限 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...