一. 總體介紹多線程
舒適提示:內核模式鎖,在不到萬不得已的狀況下,不要使用它,由於代價太大了,有不少種替代方案。函數
內核模式鎖包括:ui
①:事件鎖spa
②:信號量線程
③:互斥鎖code
④:讀寫鎖blog
⑤:動態鎖繼承
二. 事件鎖事件
事件鎖包括:源碼
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 }