第十二节:深究内核模式锁的使用场景(自动事件锁、手动事件锁、信号量、互斥锁、读写锁、动态锁)
一. 整體介紹
溫馨提示:內核模式鎖,在不到萬不得已的情況下,不要使用它,因為代價太大了,有很多種替代方案。
內核模式鎖包括:
①:事件鎖
②:信號量
③:互斥鎖
④:讀寫鎖
⑤:動態鎖
?
二. 事件鎖
?事件鎖包括:
A. 自動事件鎖(AutoResetEvent)
使用場景:可以用此鎖實現多線程環境下某個變量的自增.
現實場景: 進站火車閘機,我們用火車票來實現進站操作.
true: 表示終止狀態,閘機中沒有火車票
false: 表示非終止狀態,閘機中此時有一張火車票
B.手動事件鎖(ManualResetEvent)
現實場景:有人看守的鐵道柵欄(和自動事件鎖不一樣,不能混用)
true: 柵欄沒有合圍,沒有阻止行人通過鐵路
false:柵欄合圍了, 阻止行人通過
* 下面案例發現,鎖不住,自增仍然是無序的輸出了.
* 核心方法:WaitOne和Set
?代碼實踐-自動事件鎖:
?
1 static AutoResetEvent autoResetLock1 = new AutoResetEvent(true);2 static AutoResetEvent autoResetLock2 = new AutoResetEvent(false);3 static int num2 = 0;4 {5 //1. 能輸出6 {7 autoResetLock1.WaitOne();8 Console.WriteLine("autoResetLock1檢驗通過,可以通行");9 autoResetLock1.Set(); 10 } 11 12 //2. 不能輸出 13 { 14 autoResetLock2.WaitOne(); 15 Console.WriteLine("autoResetLock2檢驗通過,可以通行"); 16 autoResetLock2.Set(); 17 } 18 19 //3.下面代碼的結果:num從0-249,有序的發現可以鎖住。 20 { 21 for (int i = 0; i < 5; i++) 22 { 23 Task.Factory.StartNew(() => 24 { 25 for (int j = 0; j < 50; j++) 26 { 27 try 28 { 29 autoResetLock1.WaitOne(); 30 Console.WriteLine(num2++); 31 autoResetLock1.Set(); 32 } 33 catch (Exception ex) 34 { 35 Console.WriteLine(ex.Message); 36 } 37 38 } 39 }); 40 } 41 } 42 }代碼實踐-手動事件鎖:
1 static int num2 = 0;2 static ManualResetEvent mreLock = new ManualResetEvent(true);3 //下面代碼鎖不住,仍然是無序的輸出了4 {5 for (int i = 0; i < 5; i++)6 {7 Task.Factory.StartNew(() =>8 {9 for (int j = 0; j < 50; j++) 10 { 11 try 12 { 13 mreLock.WaitOne(); 14 Console.WriteLine(num2++); 15 mreLock.Set(); 16 } 17 catch (Exception ex) 18 { 19 Console.WriteLine(ex.Message); 20 } 21 22 } 23 }); 24 } 25 }?
三. 信號量
信號量:
* 核心類:Semaphore,通過int數值來控制線程個數。
* 通過觀察構造函數 public Semaphore(int initialCount, int maximumCount);:
* initialCount: 可以同時授予的信號量的初始請求數。
* maximumCount: 可以同時授予的信號量的最大請求數。
*?static Semaphore seLock = new Semaphore(1, 1);?//表示只允許一個線程通過
* 下面的案例可以有序的輸出。
* 核心方法:WaitOne和Release
?代碼實踐:
1 ?static Semaphore seLock = new Semaphore(1, 1); //只允許一個線程通過?2 //下面代碼鎖住了,可以有序的輸出3 {4 for (int i = 0; i < 5; i++)5 {6 Task.Factory.StartNew(() =>7 {8 for (int j = 0; j < 50; j++)9 { 10 try 11 { 12 seLock.WaitOne(); 13 Console.WriteLine(num2++); 14 seLock.Release(); 15 } 16 catch (Exception ex) 17 { 18 Console.WriteLine(ex.Message); 19 } 20 21 } 22 }); 23 } 24 }?
四. 互斥鎖
互斥鎖:
核心方法:WaitOne和ReleaseMutex
下面案例可以鎖住,有序輸出
總結以上三種類型的鎖,都有一個WaitOne方法,觀察源碼可知,都繼承于WaitHandle類。
?代碼實踐:
1 static Mutex mutex = new Mutex();2 //下面代碼鎖住了,可以有序的輸出3 {4 for (int i = 0; i < 5; i++)5 {6 Task.Factory.StartNew(() =>7 {8 for (int j = 0; j < 50; j++)9 { 10 try 11 { 12 mutex.WaitOne(); 13 Console.WriteLine(num2++); 14 mutex.ReleaseMutex(); 15 } 16 catch (Exception ex) 17 { 18 Console.WriteLine(ex.Message); 19 } 20 21 } 22 }); 23 } 24 }?
五. 讀寫鎖
? 讀寫鎖(ReaderWriterLock):
背景:多個線程讀,一個線程寫,如果寫入的時間太久,此時讀的線程會被卡死,這個時候就要用到讀寫鎖了。
鎖讀的兩個核心方法:AcquireReaderLock和ReleaseReaderLock。
鎖寫的兩個核心方法:AcquireWriterLock和ReleaseWriterLock。
?代碼實踐:
1 static ReaderWriterLock rwlock = new ReaderWriterLock();2 private void button24_Click(object sender, EventArgs e)3 {4 #region 01-讀寫鎖5 {6 //開啟5個線程執行讀操作7 for (int i = 0; i < 5; i++)8 {9 Task.Run(() => 10 { 11 Read(); 12 }); 13 } 14 //開啟1個線程執行寫操作 15 Task.Factory.StartNew(() => 16 { 17 Write(); 18 }); 19 } 20 #endregion 21 22 } 23 /// <summary> 24 /// 線程讀 25 /// </summary> 26 static void Read() 27 { 28 while (true) 29 { 30 Thread.Sleep(10); 31 rwlock.AcquireReaderLock(int.MaxValue); 32 Console.WriteLine("當前 t={0} 進行讀取 {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now); 33 rwlock.ReleaseReaderLock(); 34 } 35 } 36 /// <summary> 37 /// 線程寫 38 /// </summary> 39 static void Write() 40 { 41 while (true) 42 { 43 Thread.Sleep(300); 44 rwlock.AcquireWriterLock(int.MaxValue); 45 Console.WriteLine("當前 t={0} 進行寫入 {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now); 46 rwlock.ReleaseWriterLock(); 47 } 48 }?
六. 動態鎖
動態鎖(CountdownEvent):
* 作用:限制線程數的一個機制。
* 業務場景:有Orders、Products、Users表,我們需要多個線程從某一張表中讀取數據。
* 比如:Order表10w,10個線程讀取。(每個線程讀1w)
?Product表5w,5個線程讀取。(每個線程讀1w)
? ??User表2w,2個線程讀取。(每個線程讀1w)
三個核心方法:
①.Reset方法:重置當前的線程數量上限。(初始化的時候,默認設置一個上限)
②.Signal方法:將當前的線程數量執行減1操作。(使用一個thread,這個線程數量就會減1操作,直到為0后,繼續下一步)
③.Wait方法:相當于我們的Task.WaitAll方法。
代碼實踐:
1 //初始化線程數量上限為10.2 static CountdownEvent cdLock = new CountdownEvent(10);3 private void button25_Click(object sender, EventArgs e)4 {5 //加載Orders搞定6 cdLock.Reset(10);7 for (int i = 0; i < 10; i++)8 {9 Task.Factory.StartNew(() => 10 { 11 LoadOrder(); 12 }); 13 } 14 cdLock.Wait(); 15 Console.WriteLine("所有的Orders都加載完畢。。。。。。。。。。。。。。。。。。。。。"); 16 17 //加載Product搞定 18 cdLock.Reset(5); 19 for (int i = 0; i < 5; i++) 20 { 21 Task.Run(() => 22 { 23 LoadProduct(); 24 }); 25 } 26 cdLock.Wait(); 27 Console.WriteLine("所有的Products都加載完畢。。。。。。。。。。。。。。。。。。。。。"); 28 29 //加載Users搞定 30 cdLock.Reset(2); 31 for (int i = 0; i < 2; i++) 32 { 33 Task.Factory.StartNew(() => 34 { 35 LoadUser(); 36 }); 37 } 38 cdLock.Wait(); 39 Console.WriteLine("所有的Users都加載完畢。。。。。。。。。。。。。。。。。。。。。"); 40 41 Console.WriteLine("所有的表數據都執行結束了。。。恭喜恭喜。。。。"); 42 Console.Read(); 43 } 44 static void LoadOrder() 45 { 46 //書寫具體的業務邏輯 47 Console.WriteLine("當前LoadOrder正在加載中。。。{0}", Thread.CurrentThread.ManagedThreadId); 48 //線程數量減1 49 cdLock.Signal(); 50 51 } 52 static void LoadProduct() 53 { 54 //書寫具體的業務邏輯 55 Console.WriteLine("當前LoadProduct正在加載中。。。{0}", Thread.CurrentThread.ManagedThreadId); 56 //線程數量減1 57 cdLock.Signal(); 58 } 59 static void LoadUser() 60 { 61 //書寫具體的業務邏輯 62 Console.WriteLine("當前LoadUser正在加載中。。。{0}", Thread.CurrentThread.ManagedThreadId); 63 //線程數量減1 64 cdLock.Signal(); 65 }總結
以上是生活随笔為你收集整理的第十二节:深究内核模式锁的使用场景(自动事件锁、手动事件锁、信号量、互斥锁、读写锁、动态锁)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 曝小米13系列屏幕大升级:有望首发三星E
- 下一篇: HBO再谱权游续篇