1. 程式人生 > >【轉載】5天不再懼怕多線程——第四天 信號量

【轉載】5天不再懼怕多線程——第四天 信號量

win 釋放 對象 sem eap 調用 state logs 一份

今天整理“信號量”的相關知識,其實想想也蠻有趣的,鎖,互斥,信號量都可以實現線程同步,在framework裏面主要有三種。

<1>:ManualResetEvent

<2>:AutoResetEvent

<3>: Semaphore

好,下面就具體看看這些玩意的使用。

一:ManualResetEvent

該對象有兩種信號量狀態True和False,好奇的我們肯定想知道True和False有什麽區別,稍後的例子見分曉,有三個方法值得學習一下。

1:WaitOne

該方法用於阻塞線程,默認是無限期的阻塞,有時我們並不想這樣,而是采取超時阻塞的方法,如果超時就放棄阻塞,這樣也就避免了無限期

等待的尷尬。

2:Set

手動修改信號量為True,也就是恢復線程執行。

3:ReSet

手動修改信號量為False,暫停線程執行。

好了,下面舉個例子說明一下。

<1> 信號量初始為False,WaitOne采用無限期阻塞,可以發現線程間可以進行交互。

技術分享
 1 public class Example
2 {
3 public static void Main()
4 {
5 Thread t = new Thread(Run);
6
7 t.Name = "Jack";
8
9 Console.WriteLine("當前時間:{0} {1} {1},我是主線程,收到請回答。", DateTime.Now, t.Name);
10
11 t.Start();
12
13 Thread.Sleep(5000);
14
15 mr.Set();
16
17 Console.Read();
18 }
19
20 static ManualResetEvent mr = new ManualResetEvent(false);
21
22 static void Run()
23 {
24 mr.WaitOne();
25
26 Console.WriteLine("\n當前時間:{0} 主線程,主線程,{1}已收到!", DateTime.Now, Thread.CurrentThread.Name);
27 }
28 }
技術分享

技術分享

<2> 信號量初始為True,WaitOne采用無限期阻塞,實驗發現WaitOne其實並沒有被阻塞。

 static ManualResetEvent mr = new ManualResetEvent(true);

技術分享

<3>信號量初始為False,WaitOne采用超時2s,雖然主線程要等5s才能進行Set操作,但是WaitOne已經等不及提前執行了。

技術分享
 1 public class Example
2 {
3 public static void Main()
4 {
5 Thread t = new Thread(Run);
6
7 t.Name = "Jack";
8
9 Console.WriteLine("當前時間:{0} {1} {1},我是主線程,收到請回答。", DateTime.Now, t.Name);
10
11 t.Start();
12
13 Thread.Sleep(5000);
14
15 mr.Set();
16
17 Console.Read();
18 }
19
20 static ManualResetEvent mr = new ManualResetEvent(false);
21
22 static void Run()
23 {
24 mr.WaitOne(2000);
25
26 Console.WriteLine("\n當前時間:{0} 主線程,主線程,{1}已收到!", DateTime.Now, Thread.CurrentThread.Name);
27 }
28 }
技術分享

技術分享


二:AutoResetEvent

在VS對象瀏覽器中,我們發現AutoResetEvent和ManualResetEvent都是繼承於EventWaitHandle,所以基本功能是一樣的,不過值得註意

的一個區別是WaitOne會改變信號量的值,比如說初始信號量為True,如果WaitOne超時信號量將自動變為False,而ManualResetEvent則不會。

技術分享
 1 public class Example
2 {
3 public static void Main()
4 {
5 Thread t = new Thread(Run);
6
7 t.Name = "Jack";
8
9 t.Start();
10
11 Console.Read();
12 }
13
14 static AutoResetEvent ar = new AutoResetEvent(true);
15
16 static void Run()
17 {
18 var state = ar.WaitOne(1000, true);
19
20 Console.WriteLine("我當前的信號量狀態:{0}", state);
21
22 state = ar.WaitOne(1000, true);
23
24 Console.WriteLine("我恨你,不理我,您現在的狀態是:{0}", state);
25
26 }
27 }
技術分享

技術分享

三:Semaphore

這玩意是.net 4.0新增的,用於控制線程的訪問數量,默認的構造函數為initialCount和maximumCount,表示默認設置的信號量個數和

最大信號量個數,其實說到底,裏面是采用計數器來來分配信號量,當你WaitOne的時候,信號量自減,當Release的時候,信號量自增,然而

當信號量為0的時候,後續的線程就不能拿到WaitOne了,所以必須等待先前的線程通過Release來釋放。

好了,下面還是舉例子來說明一下:

<1> initialCount=1,maximunCount=10,WaitOne采用無限期等待。

技術分享
 1 namespace ConsoleApplication3
2 {
3 class Program
4 {
5 static void Main(string[] args)
6 {
7
8 Thread t1 = new Thread(Run1);
9 t1.Start();
10
11 Thread t2 = new Thread(Run2);
12 t2.Start();
13
14 Console.Read();
15 }
16
17 static Semaphore sem = new Semaphore(1, 10);
18
19 static void Run1()
20 {
21 sem.WaitOne();
22
23 Console.WriteLine("大家好,我是Run1");
24 }
25
26 static void Run2()
27 {
28 sem.WaitOne();
29
30 Console.WriteLine("大家好,我是Run2");
31 }
32 }
33 }
技術分享

技術分享

我們悲劇的發現t2線程不能執行,我們知道WaitOne相當於自減信號量,然而默認的信號量個數為1,所以t2想執行必須等待t1通過Release來釋放。

技術分享
1         static void Run1()
2 {
3 sem.WaitOne();
4
5 Console.WriteLine("大家好,我是Run1");
6
7 sem.Release();
8 }
技術分享

可能有的同學要問,我不是設置了maximunCount=10嗎?為什麽沒有起到作用?是的,默認情況下是沒有起到作用,必須要我們手動幹預一下,

我們知道調用Release方法相當於自增一個信號量,然而Release有一個重載,可以指定自增到maximunCount個信號量,這裏我就在主線程上

Release(10),看看效果。

技術分享
 1 namespace ConsoleApplication3
2 {
3 class Program
4 {
5 static void Main(string[] args)
6 {
7
8 Thread t1 = new Thread(Run1);
9 t1.Start();
10
11 Thread t2 = new Thread(Run2);
12 t2.Start();
13
14 Thread.Sleep(1000);
15
16 sem.Release(10);
17
18 Console.Read();
19 }
20
21 static Semaphore sem = new Semaphore(1, 10);
22
23 static void Run1()
24 {
25 sem.WaitOne();
26
27 Console.WriteLine("大家好,我是Run1");
28 }
29
30 static void Run2()
31 {
32 sem.WaitOne();
33
34 Console.WriteLine("大家好,我是Run2");
35 }
36 }
37 }
技術分享

技術分享


<2> Semaphore命名,升級進程交互。

在VS對象瀏覽器中發現Semaphore是繼承字WaitHandle,而WaitHandle封裝了win32的一些同步機制,所以當我們給Semaphore命名的時候

就會在系統中可見,下面舉個例子,把下面的代碼copy一份,運行兩個程序。

技術分享
 1 namespace ConsoleApplication3
2 {
3 class Program
4 {
5 static void Main(string[] args)
6 {
7
8 Thread t1 = new Thread(Run1);
9 t1.Start();
10
11 Thread t2 = new Thread(Run2);
12 t2.Start();
13
14 Console.Read();
15 }
16
17 static Semaphore sem = new Semaphore(3, 10, "cnblogs");
18
19 static void Run1()
20 {
21 sem.WaitOne();
22
23 Console.WriteLine("當前時間:{0} 大家好,我是Run1", DateTime.Now);
24 }
25
26 static void Run2()
27 {
28 sem.WaitOne();
29
30 Console.WriteLine("當前時間:{0} 大家好,我是Run2", DateTime.Now);
31 }
32 }
33 }
技術分享

技術分享

是的,我設置了信號量是3個,所以只能有三個線程持有WaitOne,後續的線程只能苦苦的等待。

轉載地址:http://www.cnblogs.com/huangxincheng/archive/2012/03/17/2404181.html

【轉載】5天不再懼怕多線程——第四天 信號量