關於c#多線程中的幾個信號量

来源:https://www.cnblogs.com/lonely-wen/archive/2022/05/29/16325316.html
-Advertisement-
Play Games

#信號量在c#多線程通信中主要用來向阻塞的線程傳達信號從而使得阻塞線程繼續執行 多線程信號(線程交互):通常是指線程必須等待一個線程或者多個線程通知交互(釋放信號)才可以繼續執行 在c#中信號量主要有這幾個 AutoResetEvent,ManualResetEvent,CountdownEvent ...


信號量在c#多線程通信中主要用來向阻塞的線程傳達信號從而使得阻塞線程繼續執行

多線程信號(線程交互):通常是指線程必須等待一個線程或者多個線程通知交互(釋放信號)才可以繼續執行
在c#中信號量主要有這幾個 AutoResetEvent,ManualResetEvent,CountdownEvent,EventWaitHandle,Semaphore

AutoResetEvent

AutoResetEvent 在釋放信號量後,會預設設置為無信號狀態。AutoResetEvent 構造函數會傳遞一個initialState boolean 類型的參數,參數為false 時 需要主動去傳遞信
號量,傳遞信號量之後將重新設置為無信號狀態。參數為ture 時會自動設置為有信號狀態(終止狀態),大體意思就是,會預設執行阻塞線程,不需要阻塞線程收到信號量才會執
行(不會阻塞調用線程)。在參數為ture 時,AutoResetEvent 類實例調用 Reset () 方法後,會將當前AutoResetEvent 類實例設置為無信號狀態也就是 變成了一個 參數為
false 的 AutoResetEvent 類實例,在此之後的執行阻塞線程都需要主動去釋放(傳遞)信號。

private static AutoResetEvent auto = new AutoResetEvent(false);
private static AutoResetEvent auto = new AutoResetEvent(ture);//有信號終止狀態
Thread thread1 = new Thread(AutoResetEventHandler);
            Console.WriteLine("當前線程id"+Thread.CurrentThread.ManagedThreadId);
            thread1.Start();
            Thread.Sleep(5000);
            auto.Set();
           // auto.Reset();  在這種情況下new AutoResetEvent(ture) 的 類實例 會變成無信號未終止狀態的 如果阻塞線程沒有接收到信號量將會一直阻塞下去,直到接收到信號量
            Thread thread2 = new Thread(AutoResetEventHandlerTwo);
            thread2.Start();
            Thread.Sleep(3000);//等待3秒
private static void AutoResetEventHandler()
        {
            Console.WriteLine("當前線程id" + Thread.CurrentThread.ManagedThreadId);
            auto.WaitOne();//阻塞線程
            Console.WriteLine("等待一秒後執行");
        }
private static void AutoResetEventHandlerTwo()
        {
            auto.WaitOne();//阻塞線程
            Console.WriteLine("我是第二個等待執行");
        }

ManualResetEvent

ManualResetEvent 與上面的AutoResetEvent 類似在構造函數中也會傳入一個Boolean類型參數,不同的是信號量的釋放,AutoResetEvent在信號量釋放後會自動設置為無信號狀態(未終止狀態),ManualResetEvent 需要我們手動調用Reset()方法將其設置為無信號量狀態(未終止狀態),否則其會一直保持有信號量狀態(終止狀態)ManualResetEvent 如果不手動重置信號量狀態,阻塞線程將不會起作用,會立即執行。

private static ManualResetEvent manualReset = new ManualResetEvent(false);
private static ManualResetEvent manualReset = new ManualResetEvent(true);
Thread thread1 = new Thread(() => {
                manualReset.WaitOne();
                Console.WriteLine("最開始的執行");
            });
            thread1.Start();
            Thread.Sleep(3000);//休眠--等待三秒
            manualReset.Set();//釋放信號量
            Thread thread2 = new Thread(ManualResetEventHandler1);
            thread2.Start();
            manualReset.Reset();//充值信號量
            Thread thread3=new Thread(ManualResetEventHandler2);
            manualReset.Set();//釋放信號量
            thread3.Start();
            manualReset.Reset();
private static void ManualResetEventHandler1()
        {
            manualReset.WaitOne();
            Console.WriteLine("第一次等待執行");
        }
        private static void ManualResetEventHandler2()
        {
            manualReset.WaitOne();
            Console.WriteLine("第二次等待執行");
        }

上面說到過,ManualResetEvent 構造函數與AutoResetEvent構造函數是一樣,通過bool類型的參數判讀 類的實例是否預設釋放信號量,不同的是ManualResetEvent 需要手動調用Reset()方法。上面代碼中,我們傳遞了一個false參數,調用了Set()方法釋放信號量,然後再調用Reset()方法重置信號量,如此反覆一次,ManualResetEventHandler2 會一直阻塞 直到我們釋放信號量,才會繼續執行。

CountdownEvent

CountdownEvent 實例化是需要傳入一個int 類型作為InitialCount初始值,CountdownEvent信號量的釋放很特別,只有當Countdown類的實例的CurrentCount等於0時才會釋放我們的信號量,Signal()方法每次調用都會使得CurrentCount進行-1操作。Reset()方法會重置為實例化對象時傳遞的參數值,也可以Reset(100)對我們的InitialCount重新賦值。

private static CountdownEvent countdownEvent = new CountdownEvent(1000);
 CountReduce();
           // countdownThread.Start();
            Thread thread = new Thread(() => {
                countdownEvent.Wait();
                Console.WriteLine("直到CountdownEvent總數="+countdownEvent.CurrentCount+"我才執行");
                //CountdownEvent.CurrentCount//當前總數
                //CountdownEvent.AddCount()//添加1
                //CountdownEvent.AddCount(10);//添加指定數量
                //CountdownEvent.InitialCount//總數
                //CountdownEvent.Reset()//設置為InitialCount初始值
                //CountdownEvent.Reset(100)//設置為指定初始值
            });
            thread.Start();
private static async Task CountReduce()
        {
           await Task.Run(async () => {
               for(var i = 0; i < 1000; i++)
                {
                    await Task.Delay(100);//休眠100毫秒--等到100毫秒
                   //if (countdownEvent.CurrentCount < 10)
                   //{
                   //    countdownEvent.Reset(100);
                   //    CountReduce();
                   //}
                   countdownEvent.Signal();
                    Console.WriteLine("當前總數"+countdownEvent.CurrentCount);
                }
            });
        }

上面代碼中我們有用到非同步方法但沒有等待結果,但是但是線程的委托方法中調用了 countdownEvent.Wait()來阻塞線程;,只有當我們的CurrentCount等於0時才會釋放信號量線程才不會阻塞得以繼續執行(有感興趣的可以試試這部分的代碼)

EventWaitHandle

本地事件等待句柄是指創建EventWaitHandle 對象時指定EventResetMode枚舉,可分為自動重置的事件等待句柄和手動重置的事件等待句柄。
關於事件等待句柄,不涉及到.NET 事件以及委托和事件處理程式,我們可以看一下官方的聲明。

EvenetResetMode.AutoReset

看到AutoReset是不是想起了,我們上面的AutoResetEvent,其用法是一樣的。在創建EventWaitHanlde對象時來指定是否自動重置信號狀態。此同步事件表示一個等待線程(阻塞線程)在收到信號時自動重置信號狀態。此事件向等待線程發送信號時,需要調用Set()方法

EvenetResetMode.ManualReset

在創建EventWaitHandle對象時指定手動重置信號狀態。事件收到信號時手動重置信號狀態,調用Set()方法釋放信號。在調用ReSet()方法前,在此事件等待句柄上的一個或多個等待線程(阻塞線程)收到信號,立即繼續執行,並且此時的等待事件句柄一直時保持信號狀態(終止狀態)。這裡有個註意點,EventReseMode.ManualReset等待句柄上有一個或多個等待線程,我們要註意Rese()的時機,等待線程恢復執行前是需要一定的執行時間的,我們無法判斷那個等待線程恢復到執行前,在調用Reset()方法可能會中斷等待線程的執行。如果我們希望在所有的等待線程都執行完後開啟新的線程,就必須將他組織到等待線程都完成後去發送新的信號量執行新的任務。
這裡我們可以看看官方的說法

private static EventWaitHandle EventWaitHandle=new EventWaitHandle(false,EventResetMode.ManualReset);
Thread thread1 = new Thread(EventWaitHandle1);
            Thread thread2 = new Thread(EventWaitHandle2);
            Thread thread3 = new Thread(EventWaitHandle3);
            thread1.Start();
            thread2.Start();
            thread3.Start();
            EventWaitHandle.Set();
            Thread thread4 = new Thread(EventWaitHandl4);
            Thread.Sleep(1000);
            thread4.Start();
            EventWaitHandle.Reset();
 private static void EventWaitHandle1()
        {
            EventWaitHandle.WaitOne();
            Thread.Sleep(1000);
            Console.WriteLine("我是第1個EventWaitHandle");
        }
        private static void EventWaitHandle2()
        {
            EventWaitHandle.WaitOne();
            Thread.Sleep(2000);
            Console.WriteLine("我是第2個EventWaitHandle");
        }
        private static void EventWaitHandle3()
        {
            EventWaitHandle.WaitOne();
            Thread.Sleep(3000);
            Console.WriteLine("我是第3個EventWaitHandle");
        }
        private static void EventWaitHandl4()
        {
            Thread.Sleep(3000);
            EventWaitHandle.WaitOne();
            Console.WriteLine("我是第4個EventWaitHandle");
        }

這裡著重說一下EventWaitHandle4,從上面可以看到,在thread4線程開始後就開始調用了Reset方法,並且在EventWaitHandle裡面休眠了三秒,這時候EventWaitHandle無法接收到信號量會一直等待下去直到接收到新的信號量。

Semaphore

Semaphore 可以限制同時進入的線程數量。Semaphore 的構造函數有兩個int 類型的參數,第一是指允許同時進入線程的個數,第二個是指最多與同時進入線程的個數,並且第二個參數時不能小於第一個參數(畢竟同時進入的不能大於最大能容納下的)。WaitOne()方法這裡的與上面幾個信號量有點小小的不同,每調用一次Semaphore釋放的信號燈數量減一,當信號燈數量為0時會阻塞線程,Release()方法會對我們的信號燈數量進行加一操作(釋放信號燈),也可以調用Release(int i)來指定釋放的信號燈數量。這裡有個註意點,我們可以在程式中多次調用Release方法(),但要保證在程式中釋放的信號量不能大於最大信號量。

private static Semaphore semaphore = new Semaphore(2, 5);//本地信號燈
for (var i = 0; i < 12; i++)
            {
                Thread thread = new Thread(new ParameterizedThreadStart(Semaphorehandle));
                thread.Start(i);
            }
 private static void Semaphorehandle(Object i)
        {
            semaphore.WaitOne();
            Console.WriteLine((int)i + "進入了線程");
            Thread.Sleep(2000);
            Console.WriteLine((int)i + "準備離開線程");
            if ((int)i >1)
            {
                Console.WriteLine(semaphore.Release());
                return;
            }
            semaphore.Release(2);
        }

這裡插一句——多線程執行是沒有特定的順序的、是不可預測的。
Semaphore信號燈有兩種:本地信號燈和命名系統信號燈。本地信號燈僅存在於進程中(上面的例子中使用的是本地信號燈)。命名系統信號燈是存在與整個操作系統的,一般用於同步進程的活動。
c#多線程的信號量就先到這了。
也可以看大佬的教程
本文涉及到的Demo代碼


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

-Advertisement-
Play Games
更多相關文章
  • Hi, 我是Mic。 今天分享一道一線互聯網公司必問的面試題。 ”JVM如何判斷一個對象可以被回收“ 關於這個問題,來看看普通人和高手的回答。 普通人: 嗯。。。。。。。。。。 高手: 好的,面試官。 在JVM裡面,要判斷一個對象是否可以被回收,最重要的是判斷這個對象是否還在被使用,只有沒被使用的對 ...
  • 前言 今天的這個腳本,是一個別人發的外包,交互界面的代碼就不在這裡說了,但是可以分享下自動評論、自動點贊、自動關註、採集評論和視頻的數據是如何實現的 開發環境 python 3.8 運行代碼pycharm 2021.2 輔助敲代碼requests 第三方模塊 原理: 模擬客戶端,向伺服器發送請求 對 ...
  • 案例標題:用python可視化分析,B站Top100排行榜數據。 分析流程: 一、數據讀取 二、數據概覽 三、數據清洗 四、可視化分析 ·相關性分析-散點圖(scatter) ·得分分佈-餅圖(pie) ·各指標分佈-箱形圖(boxplot) ·視頻作者分析-詞雲圖(wordcloud) ...
  • 0、前言 這篇博客是給認識的那幫新手搞的,剛進入IT行業的崽們 這個東西配置好了,也可以選擇弄成線上文檔,下一次安裝IDEA時,有一個import導入配置,然後就可以自己配置好了( 雖然方便,但不建議用 ) 另外:IDEA建議別漢化,一開始接觸不習慣,後續使用一段時間之後就很舒服了 jetBrain ...
  • 一、SpringMVC使用 1.工程創建 創建maven工程。 添加java、resources目錄。 引入Spring-webmvc 依賴。 <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc< ...
  • 前後連續的電腦語句組合在一起並有名字可供調用稱之為函數,本章主要介紹如何在程式中定義和使用函數其中包括如何調用函數、使用參數、使用參數的*和**運算,返回數值。如果把電腦語句比為武術動作的話,那麼函數就可以比喻為功夫“套路”,本質上“降龍十八掌”就是包含了18個語句的函數,那麼“葵花寶典”呢? ...
  • .NET CORE 1.Microsoft Azure 微軟擁抱雲計算 2..net core 是為雲所生的技術 3.Net Framework缺點: 系統級別的安裝,互相影響 無法獨立部署 SAP.NET和IIS深度耦合 非雲原生 4.NET Framework歷史包袱 基於拖控制項之上的MVC A ...
  • RBAC實現最基礎的許可權管理webapi+vue 一、確定關係表 //許可權管理表 這個表可以實現左側菜單的顯示 [Table("Permission")] public class Permission { [Key] public int Id { get; set; } public strin ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...