【.NET深呼吸】線程信號量(Semaphore)


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.cnblogs.com/files/tcjiaan/DemoApp.zip

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM