第十二節:深究內核模式鎖的使用場景(自動事件鎖、手動事件鎖、信號量、互斥鎖、讀寫鎖、動態鎖)

一. 總體介紹多線程

舒適提示:內核模式鎖,在不到萬不得已的狀況下,不要使用它,由於代價太大了,有不少種替代方案。函數

  內核模式鎖包括: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         }

 

 

 

 

 

相關文章
相關標籤/搜索