Semaphore類可以控制某個資源允許訪問的線程數,Semaphore有命名式的,也有不命名的;如果不考慮跨進程工作,一般在代碼中使用不命名方式即可。
信號量有點類似于等待句柄,某個線程如果調用了WaitOne方法,這個線程就會暫停,并且等待有可用的信號量時才會繼續執行;某個線程調用Release方法,就會釋放一個信號計數值,每調用一次就釋放一個,如果想一次性釋放N個信號,可以調用Release(int)重載,把要釋放的數量傳遞給方法參數,但這個數值不能超過Semaphore實例化時所指定的最大值,否則會引發異常。
Semaphore構造函數可以指定允許的最大信號量,以及默認的信號量。聲明如下:
Semaphore(int initialCount, int maximumCount);
maximumCount參數指定該對象允許的最大信號量;initialCount參數指定默認值,這個默認值不能超過maximumCount指定的最大值。即該Semaphore實例默認允許多少個線程收到信號(訪問資源)。
當某個占用資源的線程調用Release方法后,它會釋放出一個或多個信號,這時候,其他等待的線程就可以繼續執行。
只要是涉及到線程問題都特別難說清楚,相當抽象,相當考驗人的理解能力。
比如,圖書館里面有五本《X瓶梅》,但想借這本書的有20人。前面五個人自然很輕松就借到(進入訪問圈,這五個線程以外的線程等待),其他人只好等了。
過了幾天后,有個家伙通宵看書,終于看完了,因此他還了書,這時候,剩下的15個人看誰的動作快,可以借到剛還回去的這本書。
再過了幾天,又有兩個人看完了,還書。此時,剩下的14個人中,有兩個人可以借得此書。
大概的原理就是這樣,下面看看例子。
class PRogram { // 生成隨機數,以延遲每個任務的執行時間 static Random rand = new Random(); // 聲明Semaphore變量,以控制線程信號量 static Semaphore sm = null; static void Main(string[] args) { sm = new Semaphore(1, 4); //實例化 // 啟動10個任務 for (int i = 0; i < 10; i++) { Task t = new Task(DoWork, "任務" + (i + 1)); t.Start(); } // 防止DOS窗口立即退出 Console.Read(); } private static async void DoWork(object p) { sm.WaitOne(); //等待花開 string tn = p?.ToString(); Console.WriteLine($"{tn} 已獲得訪問。"); await Task.Delay(rand.Next(1, 10) * 1000); // 釋放 sm.Release(); //花謝了 Console.WriteLine($"{tn}已釋放。"); } }
多線程開發我最喜歡用Task類,方便簡單強大好用高大上,而且它還能自行處理CPU多個核的問題。在上面例子中,有10個任務要執行,但我所實例化的Semaphore對象給的最大訪問線程數為4,而默認狀態下只允許1個線程同時訪問。
所以,10個任務啟動后,其中一個會搶到訪問權,其他任務就等吧。這時候Semaphore對象可訪問數為0。因為默認只允許1,現在有一個線程搶了,所以剩下就是0個訪問權了。
當這個搶到訪問權的任務調用Release方法后,訪問權被釋放,這時候剩下的9個任務就開始搶,誰搶到誰就執行……依此類推。
看看運行結果。
任務1手快,它先搶到了訪問權,于是它dododo,do完后,調用Release方法釋放,然后任務3人品好,就搶到了訪問權,然后XXXXX,X完后調用Release釋放。其他線程繼續搶……
估計看完以上例子后,大家應該有點頭緒了。
現在,我們把上面的代碼改一下,在初始化Semaphore對象時的默認值從1改為3。
sm = new Semaphore(3, 4);
默認允許3個線程同時訪問資源,最大數量為4。
然后再次運行,結果如下:
因為默認允許3個線程同時進入,所以在輸出結果中,前面三個任務都能獲取訪問權,而其他的任務只能等待機會。當前面已獲得資源的三個任務中有一個或者N個進行釋放后,剩下的任務又開始搶機會。
本文示例下載地址:http://files.VEVb.com/files/tcjiaan/DemoApp.zip
新聞熱點
疑難解答