C#的多線程機制探索(續(xù)3)
發(fā)表時間:2024-06-19 來源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]五、互斥對象——更加靈活的同步方式有時候你會覺得上面介紹的方法好像不夠用,對,我們解決了代碼和資源的同步問題,解決了多線程自動化管理和定時觸發(fā)的問題,但是如何控制多個線程相互之間的聯(lián)系呢?例如我要到餐廳吃飯,在吃飯之前我先得等待廚師把飯菜做好,之后我開始吃飯,吃完我還得付款,付款方式可以是現(xiàn)金...
五、互斥對象——更加靈活的同步方式
有時候你會覺得上面介紹的方法好像不夠用,對,我們解決了代碼和資源的同步問題,解決了多線程自動化管理和定時觸發(fā)的問題,但是如何控制多個線程相互之間的聯(lián)系呢?例如我要到餐廳吃飯,在吃飯之前我先得等待廚師把飯菜做好,之后我開始吃飯,吃完我還得付款,付款方式可以是現(xiàn)金,也可以是信用卡,付款之后我才能離開。分析一下這個過程,我吃飯可以看作是主線程,廚師做飯又是一個線程,服務(wù)員用信用卡收款和收現(xiàn)金可以看作另外兩個線程,大家可以很清楚地看到其中的關(guān)系——我吃飯必須等待廚師做飯,然后等待兩個收款線程之中任意一個的完成,然后我吃飯這個線程可以執(zhí)行離開這個步驟,于是我吃飯才算結(jié)束了。事實上,現(xiàn)實中有著比這更復(fù)雜的聯(lián)系,我們怎樣才能很好地控制它們而不產(chǎn)生沖突和重復(fù)呢?
這種情況下,我們需要用到互斥對象,即System.Threading命名空間中的Mutex類。大家一定坐過出租車吧,事實上我們可以把Mutex看作一個出租車,那么乘客就是線程了,乘客首先得等車,然后上車,最后下車,當一個乘客在車上時,其他乘客就只有等他下車以后才可以上車。而線程與Mutex對象的關(guān)系也正是如此,線程使用Mutex.WaitOne()方法等待Mutex對象被釋放,如果它等待的Mutex對象被釋放了,它就自動擁有這個對象,直到它調(diào)用Mutex.ReleaseMutex()方法釋放這個對象,而在此期間,其他想要獲取這個Mutex對象的線程都只有等待。
下面這個例子使用了Mutex對象來同步四個線程,主線程等待四個線程的結(jié)束,而這四個線程的運行又是與兩個Mutex對象相關(guān)聯(lián)的。其中還用到AutoResetEvent類的對象,如同上面提到的ManualResetEvent對象一樣,大家可以把它簡單地理解為一個信號燈,使用AutoResetEvent.Set()方法可以設(shè)置它為有信號狀態(tài),而使用AutoResetEvent.Reset()方法把它設(shè)置為無信號狀態(tài)。這里用它的有信號狀態(tài)來表示一個線程的結(jié)束。
// Mutex.cs using System; using System.Threading;
public class MutexSample { static Mutex gM1; static Mutex gM2; const int ITERS = 100; static AutoResetEvent Event1 = new AutoResetEvent(false); static AutoResetEvent Event2 = new AutoResetEvent(false); static AutoResetEvent Event3 = new AutoResetEvent(false); static AutoResetEvent Event4 = new AutoResetEvent(false);
public static void Main(String[] args) { 。牐燙onsole.WriteLine("Mutex Sample ..."); //創(chuàng)建一個Mutex對象,并且命名為MyMutex 。牐爂M1 = new Mutex(true,"MyMutex"); 。牐//創(chuàng)建一個未命名的Mutex 對象. gM2 = new Mutex(true); 。牐燙onsole.WriteLine(" - Main Owns gM1 and gM2");
。牐燗utoResetEvent[] evs = new AutoResetEvent[4]; 。牐爀vs[0] = Event1; file://為后面的線程t1,t2,t3,t4定義AutoResetEvent對象 。牐爀vs[1] = Event2; evs[2] = Event3; 。牐爀vs[3] = Event4;
。牐燤utexSample tm = new MutexSample( ); 。牐燭hread t1 = new Thread(new ThreadStart(tm.t1Start)); 。牐燭hread t2 = new Thread(new ThreadStart(tm.t2Start)); 。牐燭hread t3 = new Thread(new ThreadStart(tm.t3Start)); 。牐燭hread t4 = new Thread(new ThreadStart(tm.t4Start)); 。牐爐1.Start( );// 使用Mutex.WaitAll()方法等待一個Mutex數(shù)組中的對象全部被釋放 。牐爐2.Start( );// 使用Mutex.WaitOne()方法等待gM1的釋放 。牐爐3.Start( );// 使用Mutex.WaitAny()方法等待一個Mutex數(shù)組中任意一個對象被釋放 t4.Start( );// 使用Mutex.WaitOne()方法等待gM2的釋放
。牐燭hread.Sleep(2000); 。牐燙onsole.WriteLine(" - Main releases gM1"); 。牐爂M1.ReleaseMutex( ); file://線程t2,t3結(jié)束條件滿足
。牐燭hread.Sleep(1000); 。牐燙onsole.WriteLine(" - Main releases gM2"); gM2.ReleaseMutex( ); file://線程t1,t4結(jié)束條件滿足
。牐//等待所有四個線程結(jié)束 。牐燱aitHandle.WaitAll(evs); 。牐燙onsole.WriteLine("... Mutex Sample"); 。牐燙onsole.ReadLine(); }
public void t1Start( ) { 。牐燙onsole.WriteLine("t1Start started, Mutex.WaitAll(Mutex[])"); 。牐燤utex[] gMs = new Mutex[2]; 。牐爂Ms[0] = gM1;//創(chuàng)建一個Mutex數(shù)組作為Mutex.WaitAll()方法的參數(shù) 。牐爂Ms[1] = gM2; 。牐燤utex.WaitAll(gMs);//等待gM1和gM2都被釋放 。牐燭hread.Sleep(2000); 。牐燙onsole.WriteLine("t1Start finished, Mutex.WaitAll(Mutex[]) satisfied"); 。牐燛vent1.Set( ); file://線程結(jié)束,將Event1設(shè)置為有信號狀態(tài) }
public void t2Start( ) { 。牐燙onsole.WriteLine("t2Start started, gM1.WaitOne( )"); gM1.WaitOne( );//等待gM1的釋放 。牐燙onsole.WriteLine("t2Start finished, gM1.WaitOne( ) satisfied"); 。牐燛vent2.Set( );//線程結(jié)束,將Event2設(shè)置為有信號狀態(tài) }
public void t3Start( ) { 。牐燙onsole.WriteLine("t3Start started, Mutex.WaitAny(Mutex[])"); 。牐燤utex[] gMs = new Mutex[2]; gMs[0] = gM1;//創(chuàng)建一個Mutex數(shù)組作為Mutex.WaitAny()方法的參數(shù) 。牐爂Ms[1] = gM2; Mutex.WaitAny(gMs);//等待數(shù)組中任意一個Mutex對象被釋放 。牐燙onsole.WriteLine("t3Start finished, Mutex.WaitAny(Mutex[])"); Event3.Set( );//線程結(jié)束,將Event3設(shè)置為有信號狀態(tài) }
public void t4Start( ) { 。牐燙onsole.WriteLine("t4Start started, gM2.WaitOne( )"); 。牐爂M2.WaitOne( );//等待gM2被釋放 。牐燙onsole.WriteLine("t4Start finished, gM2.WaitOne( )"); Event4.Set( );//線程結(jié)束,將Event4設(shè)置為有信號狀態(tài) } }
|