1. 程式人生 > WINDOWS開發 >C# dotnet 的鎖 SemaphoreSlim 和佇列

C# dotnet 的鎖 SemaphoreSlim 和佇列

原文:C# dotnet 的鎖 SemaphoreSlim 和佇列

本文主要是試驗在順序進入等待 SemaphoreSlim 的任務是否會按照順序經過鎖執行

我在一個有趣的WPF程式裡面,需要限制任務同時執行的執行緒數量,不然使用者就會說用我的程式會讓電腦卡渣。而我的任務是需要按照指定順序執行的,我需要每次同時僅執行10個任務,同時任務執行按照傳入的順序

此時可以用到 SemaphoreSlim 這個類,這個類的作用如下,給定初始的可以通過鎖的數量,以及這個最大可以通過鎖的數量。通過 Wait 方法進行等待,如果當前已經有超過可以通過的任務通過了,那麼在 Wait 方法將會阻塞。如果沒有超過可以通過的數量,那麼將可以通過

使用 Release 方法可以新增一個或多個可以通過的數量,但是可以通過的數量最大不會超過初始化時傳入的最大可以通過鎖的數量的值

如下面程式碼

            var semaphoreSlim = new SemaphoreSlim(10,20);

此時表示初始化的時候,可以讓 10 個任務通過鎖,也就是初始化的時候可以有10次呼叫 Wait 方法能通過

而第二個引數表示最大的可以通過的數量,通過 Release 可以新增一個或多個可以通過鎖的任務,如 semaphoreSlim.Release(100); 表示設定有 100 個可以通過鎖的任務,但是實際上在上面程式碼裡面,因為設定了最大值是 20 也就是即使寫 100 其實之後能通過的任務只有 20 個

而本文的測試在於我有任務按照順序呼叫 Wait 方法進入等待,如我的任務有序號,我按照任務1 任務2 任務3 的順序呼叫Wait方法,同時此時的鎖的可以通過的數量是 0 也就是所有任務在等待

之後我通過 Release 方法的不斷呼叫,請問此時通過鎖的任務是否和佇列一樣,先等待的任務就先通過鎖。答案是這樣的

先呼叫 Wait 方法的任務,在鎖開始釋放的時候就先通過,我通過一個有趣的程式碼用來測試

我需要有很多執行緒進入鎖的 Wait 方法,但是這些執行緒每個執行緒是一個任務,這些任務有順序,進入等待方法的時候按照順序進入

而小夥伴都知道,建立執行緒的先後順序不會等於執行緒執行的先後順序,所以我使用了 AutoResetEvent 線上程建立然後執行開始之後再建立下一個執行緒

先通過 SemaphoreSlim 建立一個初始值是 10 而最大值是 10 的鎖,然後建立一個 AutoResetEvent 設定預設能通過一次

            var semaphoreSlim = new SemaphoreSlim(10,10);

        private static readonly AutoResetEvent _autoResetEvent = new AutoResetEvent(true);

接下來進入迴圈建立執行緒,建立執行緒的時候先等待 AutoResetEvent 鎖,而線上程執行的時候釋放 AutoResetEvent 鎖,這樣就能讓執行緒一定是在上一個執行緒執行之後再建立。而設定 AutoResetEvent 的初始值是通過,也就是第一個執行緒可以建立,但第二個執行緒需要等待第一個執行緒開始執行再建立

            for (int i = 0; i < 1000; i++)
            {
                var n = i;
                _autoResetEvent.WaitOne();
                new Thread(() => { GeregelkunoNeawhikarcee(semaphoreSlim,n); }).Start();
            }

新增 GeregelkunoNeawhikarcee 方法,在方法進入的時候,也就是執行緒開始執行,釋放 AutoResetEvent 鎖,這樣就能讓下一個執行緒建立

            _autoResetEvent.Set();

進入等待 SemaphoreSlim 此時等待是按照執行緒建立的順序等待

            semaphoreSlim.Wait();

接下來輸出當前的任務號,主要用來除錯是否通過鎖的順序和執行緒進入等待的順序相同

            Console.WriteLine(n);

接下來通過 Thread.Sleep 模擬執行長任務

在任務執行完成之後釋放鎖讓下一個任務開始,全部程式碼放在這裡

        static void Main(string[] args)
        {
            var semaphoreSlim = new SemaphoreSlim(10,10);

            for (int i = 0; i < 1000; i++)
            {
                var n = i;
                _autoResetEvent.WaitOne();
                new Thread(() => { GeregelkunoNeawhikarcee(semaphoreSlim,n); }).Start();
            }

            Console.Read();
        }

        private static readonly AutoResetEvent _autoResetEvent = new AutoResetEvent(true);

        private static void GeregelkunoNeawhikarcee(SemaphoreSlim semaphoreSlim,int n)
        {
            Console.WriteLine($"{n} 進入");
            _autoResetEvent.Set();

            semaphoreSlim.Wait();
            Console.WriteLine(n);

            Thread.Sleep(TimeSpan.FromSeconds(1));
            semaphoreSlim.Release();
        }

可以看到程式碼是按照順序輸出的

本文程式碼放在github歡迎小夥伴訪問

技術分享圖片
本作品採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。歡迎轉載、使用、重新發布,但務必保留文章署名林德熙(包含連結:http://blog.csdn.net/lindexi_gd ),不得用於商業目的,基於本文修改後的作品務必以相同的許可釋出。如有任何疑問,請與我聯絡