C#多線程系列之資源池限制
Semaphore、SemaphoreSlim 類
兩者都可以限制同時(shí)訪問(wèn)某一資源或資源池的線程數(shù)。
這里先不扯理論,我們從案例入手,通過(guò)示例代碼,慢慢深入了解。
Semaphore 類
這里,先列出 Semaphore 類常用的 API。
其構(gòu)造函數(shù)如下:
構(gòu)造函數(shù) | 說(shuō)明 |
---|---|
Semaphore(Int32, Int32) | 初始化 Semaphore 類的新實(shí)例,并指定初始入口數(shù)和最大并發(fā)入口數(shù)。 |
Semaphore(Int32, Int32, String) | 初始化 Semaphore 類的新實(shí)例,并指定初始入口數(shù)和最大并發(fā)入口數(shù),根據(jù)需要指定系統(tǒng)信號(hào)燈對(duì)象的名稱。 |
Semaphore(Int32, Int32, String, Boolean) | 初始化 Semaphore 類的新實(shí)例,并指定初始入口數(shù)和最大并發(fā)入口數(shù),還可以選擇指定系統(tǒng)信號(hào)量對(duì)象的名稱,以及指定一個(gè)變量來(lái)接收指示是否創(chuàng)建了新系統(tǒng)信號(hào)量的值。 |
Semaphore 使用純粹的內(nèi)核時(shí)間(kernel-time)方式(等待時(shí)間很短),并且支持在不同的進(jìn)程間同步線程(像Mutex)。
Semaphore 常用方法如下:
方法 | 說(shuō)明 |
---|---|
Close() | 釋放由當(dāng)前 WaitHandle占用的所有資源。 |
OpenExisting(String) | 打開(kāi)指定名稱為信號(hào)量(如果已經(jīng)存在)。 |
Release() | 退出信號(hào)量并返回前一個(gè)計(jì)數(shù)。 |
Release(Int32) | 以指定的次數(shù)退出信號(hào)量并返回前一個(gè)計(jì)數(shù)。 |
TryOpenExisting(String, Semaphore) | 打開(kāi)指定名稱為信號(hào)量(如果已經(jīng)存在),并返回指示操作是否成功的值。 |
WaitOne() | 阻止當(dāng)前線程,直到當(dāng)前 WaitHandle 收到信號(hào)。 |
WaitOne(Int32) | 阻止當(dāng)前線程,直到當(dāng)前 WaitHandle 收到信號(hào),同時(shí)使用 32 位帶符號(hào)整數(shù)指定時(shí)間間隔(以毫秒為單位)。 |
WaitOne(Int32, Boolean) | 阻止當(dāng)前線程,直到當(dāng)前的 WaitHandle 收到信號(hào)為止,同時(shí)使用 32 位帶符號(hào)整數(shù)指定時(shí)間間隔,并指定是否在等待之前退出同步域。 |
WaitOne(TimeSpan) | 阻止當(dāng)前線程,直到當(dāng)前實(shí)例收到信號(hào),同時(shí)使用 TimeSpan 指定時(shí)間間隔。 |
WaitOne(TimeSpan, Boolean) | 阻止當(dāng)前線程,直到當(dāng)前實(shí)例收到信號(hào)為止,同時(shí)使用 TimeSpan 指定時(shí)間間隔,并指定是否在等待之前退出同步域。 |
示例
我們來(lái)直接寫代碼,這里使用 《原子操作 Interlocked》 中的示例,現(xiàn)在我們要求,采用多個(gè)線程執(zhí)行計(jì)算,但是只允許最多三個(gè)線程同時(shí)執(zhí)行運(yùn)行。
使用 Semaphore ,有四個(gè)個(gè)步驟:
new 實(shí)例化 Semaphore,并設(shè)置最大線程數(shù)、初始化時(shí)可進(jìn)入線程數(shù);
使用 .WaitOne();
獲取進(jìn)入權(quán)限(在獲得進(jìn)入權(quán)限前,線程處于阻塞狀態(tài))。
離開(kāi)時(shí)使用 Release()
釋放占用。
Close()
釋放Semaphore 對(duì)象。
《原子操作 Interlocked》 中的示例改進(jìn)如下:
class Program { // 求和 private static int sum = 0; private static Semaphore _pool; // 判斷十個(gè)線程是否結(jié)束了。 private static int isComplete = 0; // 第一個(gè)程序 static void Main(string[] args) { Console.WriteLine("執(zhí)行程序"); // 設(shè)置允許最大三個(gè)線程進(jìn)入資源池 // 一開(kāi)始設(shè)置為0,就是初始化時(shí)允許幾個(gè)線程進(jìn)入 // 這里設(shè)置為0,后面按下按鍵時(shí),可以放通三個(gè)線程 _pool = new Semaphore(0, 3); for (int i = 0; i < 10; i++) { Thread thread = new Thread(new ParameterizedThreadStart(AddOne)); thread.Start(i + 1); } Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("任意按下鍵(不要按關(guān)機(jī)鍵),可以打開(kāi)資源池"); Console.ForegroundColor = ConsoleColor.White; Console.ReadKey(); // 準(zhǔn)許三個(gè)線程進(jìn)入 _pool.Release(3); // 這里沒(méi)有任何意義,就單純?yōu)榱搜菔静榭唇Y(jié)果。 // 等待所有線程完成任務(wù) while (true) { if (isComplete >= 10) break; Thread.Sleep(TimeSpan.FromSeconds(1)); } Console.WriteLine("sum = " + sum); // 釋放池 _pool.Close(); } public static void AddOne(object n) { Console.WriteLine($" 線程{(int)n}啟動(dòng),進(jìn)入隊(duì)列"); // 進(jìn)入隊(duì)列等待 _pool.WaitOne(); Console.WriteLine($"第{(int)n}個(gè)線程進(jìn)入資源池"); // 進(jìn)入資源池 for (int i = 0; i < 10; i++) { Interlocked.Add(ref sum, 1); Thread.Sleep(TimeSpan.FromMilliseconds(500)); } // 解除占用的資源池 _pool.Release(); isComplete += 1; Console.WriteLine($" 第{(int)n}個(gè)線程退出資源池"); } }
看著代碼有點(diǎn)多,快去運(yùn)行一下,看看結(jié)果。
示例說(shuō)明
實(shí)例化 Semaphore 使用了new Semaphore(0,3);
,其構(gòu)造函數(shù)原型為
public Semaphore(int initialCount, int maximumCount);
initialCount 表示一開(kāi)始允許幾個(gè)進(jìn)程進(jìn)入資源池,如果設(shè)置為0,所有線程都不能進(jìn)入,要一直等資源池放通。
maximumCount 表示最大允許幾個(gè)線程進(jìn)入資源池。
Release()
表示退出信號(hào)量并返回前一個(gè)計(jì)數(shù)。這個(gè)計(jì)數(shù)指的是資源池還可以進(jìn)入多少個(gè)線程。
可以看一下下面的示例:
private static Semaphore _pool; static void Main(string[] args) { _pool = new Semaphore(0, 5); _pool.Release(5); new Thread(AddOne).Start(); Thread.Sleep(TimeSpan.FromSeconds(10)); _pool.Close(); } public static void AddOne() { _pool.WaitOne(); Thread.Sleep(1000); int count = _pool.Release(); Console.WriteLine("在此線程退出資源池前,資源池還有多少線程可以進(jìn)入?" + count); }
信號(hào)量
前面我們學(xué)習(xí)到 Mutex,這個(gè)類是全局操作系統(tǒng)起作用的。我們從 Mutex 和 Semphore 中,也看到了 信號(hào)量這個(gè)東西。
信號(hào)量分為兩種類型:本地信號(hào)量和命名系統(tǒng)信號(hào)量。
- 命名系統(tǒng)信號(hào)量在整個(gè)操作系統(tǒng)中均可見(jiàn),可用于同步進(jìn)程的活動(dòng)。
- 局部信號(hào)量?jī)H存在于進(jìn)程內(nèi)。
當(dāng) name 為 null 或者為空時(shí),Mutex 的信號(hào)量時(shí)局部信號(hào)量,否則 Mutex 的信號(hào)量是命名系統(tǒng)信號(hào)量。
Semaphore 的話,也是兩種情況都有。
如果使用接受名稱的構(gòu)造函數(shù)創(chuàng)建 Semaphor 對(duì)象,則該對(duì)象將與該名稱的操作系統(tǒng)信號(hào)量關(guān)聯(lián)。
兩個(gè)構(gòu)造函數(shù):
Semaphore(Int32, Int32, String) Semaphore(Int32, Int32, String, Boolean)
上面的構(gòu)造函數(shù)可以創(chuàng)建多個(gè)表示同一命名系統(tǒng)信號(hào)量的 Semaphore 對(duì)象,并可以使用 OpenExisting 方法打開(kāi)現(xiàn)有的已命名系統(tǒng)信號(hào)量。
我們上面使用的示例就是局部信號(hào)量,進(jìn)程中引用本地 Semaphore 對(duì)象的所有線程都可以使用。 每個(gè) Semaphore 對(duì)象都是單獨(dú)的本地信號(hào)量。
SemaphoreSlim類
SemaphoreSlim 跟 Semaphore 有啥關(guān)系?
微軟文檔:
SemaphoreSlim 表示對(duì)可同時(shí)訪問(wèn)資源或資源池的線程數(shù)加以限制的 Semaphore 的輕量替代。
SemaphoreSlim 不使用信號(hào)量,不支持進(jìn)程間同步,只能在進(jìn)程內(nèi)使用。
它有兩個(gè)構(gòu)造函數(shù):
構(gòu)造函數(shù) | 說(shuō)明 |
---|---|
SemaphoreSlim(Int32) | 初始化 SemaphoreSlim 類的新實(shí)例,以指定可同時(shí)授予的請(qǐng)求的初始數(shù)量。 |
SemaphoreSlim(Int32, Int32) | 初始化 SemaphoreSlim 類的新實(shí)例,同時(shí)指定可同時(shí)授予的請(qǐng)求的初始數(shù)量和最大數(shù)量。 |
示例
我們改造一下前面 Semaphore 中的示例:
class Program { // 求和 private static int sum = 0; private static SemaphoreSlim _pool; // 判斷十個(gè)線程是否結(jié)束了。 private static int isComplete = 0; static void Main(string[] args) { Console.WriteLine("執(zhí)行程序"); // 設(shè)置允許最大三個(gè)線程進(jìn)入資源池 // 一開(kāi)始設(shè)置為0,就是初始化時(shí)允許幾個(gè)線程進(jìn)入 // 這里設(shè)置為0,后面按下按鍵時(shí),可以放通三個(gè)線程 _pool = new SemaphoreSlim(0, 3); for (int i = 0; i < 10; i++) { Thread thread = new Thread(new ParameterizedThreadStart(AddOne)); thread.Start(i + 1); } Console.WriteLine("任意按下鍵(不要按關(guān)機(jī)鍵),可以打開(kāi)資源池"); Console.ReadKey(); // _pool.Release(3); // 這里沒(méi)有任何意義,就單純?yōu)榱搜菔静榭唇Y(jié)果。 // 等待所有線程完成任務(wù) while (true) { if (isComplete >= 10) break; Thread.Sleep(TimeSpan.FromSeconds(1)); } Console.WriteLine("sum = " + sum); // 釋放池 } public static void AddOne(object n) { Console.WriteLine($" 線程{(int)n}啟動(dòng),進(jìn)入隊(duì)列"); // 進(jìn)入隊(duì)列等待 _pool.Wait(); Console.WriteLine($"第{(int)n}個(gè)線程進(jìn)入資源池"); // 進(jìn)入資源池 for (int i = 0; i < 10; i++) { Interlocked.Add(ref sum, 1); Thread.Sleep(TimeSpan.FromMilliseconds(200)); } // 解除占用的資源池 _pool.Release(); isComplete += 1; Console.WriteLine($" 第{(int)n}個(gè)線程退出資源池"); } }
SemaphoreSlim 不需要 Close()
。
兩者在代碼上的區(qū)別是就這么簡(jiǎn)單。
區(qū)別
如果使用下面的構(gòu)造函數(shù)實(shí)例化 Semaphor(參數(shù)name不能為空),那么創(chuàng)建的對(duì)象在整個(gè)操作系統(tǒng)內(nèi)都有效。
public Semaphore (int initialCount, int maximumCount, string name);
Semaphorslim 則只在進(jìn)程內(nèi)內(nèi)有效。
SemaphoreSlim 類不會(huì)對(duì) Wait
、WaitAsync
和 Release
方法的調(diào)用強(qiáng)制執(zhí)行線程或任務(wù)標(biāo)識(shí)。
而 Semaphor 類,會(huì)對(duì)此進(jìn)行嚴(yán)格監(jiān)控,如果對(duì)應(yīng)調(diào)用數(shù)量不一致,會(huì)出現(xiàn)異常。
此外,如果使用 SemaphoreSlim(Int32 maximumCount) 構(gòu)造函數(shù)來(lái)實(shí)例化 SemaphoreSlim 對(duì)象,獲取其 CurrentCount 屬性,其值可能會(huì)大于 maximumCount。 編程人員應(yīng)負(fù)責(zé)確保調(diào)用一個(gè) Wait 或 WaitAsync 方法,便調(diào)用一個(gè) Release。
這就好像筆筒里面的筆,沒(méi)有監(jiān)控,使用這使用完畢后,都應(yīng)該將筆放進(jìn)去。如果原先有10支筆,每次使用不放進(jìn)去,或者將別的地方的筆放進(jìn)去,那么最后數(shù)量就不是10了。
到此這篇關(guān)于C#多線程系列之資源池限制的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- C#多線程系列之工作流實(shí)現(xiàn)
- C#多線程系列之任務(wù)基礎(chǔ)(三)
- C#多線程系列之任務(wù)基礎(chǔ)(二)
- C#多線程系列之任務(wù)基礎(chǔ)(一)
- C#多線程系列之線程池
- C#多線程系列之線程等待
- C#多線程系列之讀寫鎖
- C#多線程系列之多階段并行線程
- C#多線程系列之線程完成數(shù)
- C#多線程系列之手動(dòng)線程通知
- C#多線程系列之線程通知
- C#多線程系列之進(jìn)程同步Mutex類
- C#多線程系列之原子操作
- C#多線程系列之多線程鎖lock和Monitor
- C#多線程系列之線程的創(chuàng)建和生命周期
- C#多線程系列之a(chǎn)sync和await用法詳解
相關(guān)文章
C#實(shí)現(xiàn)計(jì)算年齡的簡(jiǎn)單方法匯總
本文給大家分享的是C#代碼實(shí)現(xiàn)的簡(jiǎn)單實(shí)用的給出用戶的出生日期,計(jì)算出用戶的年齡的代碼,另外附上其他網(wǎng)友的方法,算是對(duì)計(jì)算年齡的一次小結(jié),希望大家能夠喜歡。2015-05-05C#實(shí)現(xiàn)遞歸算法經(jīng)典實(shí)例
這篇文章主要為大家介紹了C#實(shí)現(xiàn)遞歸算法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2022-01-01C# HttpClient Cookie驗(yàn)證解決方法
本文將詳細(xì)介紹C# HttpClient Cookie驗(yàn)證解決方法,需要了解的朋友可以參考下2012-11-11Unity3D UGUI實(shí)現(xiàn)翻書(shū)特效
這篇文章主要為大家詳細(xì)介紹了Unity3D UGUI實(shí)現(xiàn)翻書(shū)特效,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11