C#線程同步技術(shù)之Monitor
在上一講介紹了使用lock來實(shí)現(xiàn)線程之間的同步。實(shí)際上,這個(gè)lock是C#線程的一個(gè)障眼法,在C#編譯器編譯lock語句時(shí),將其編譯成了調(diào)用Monitor類。先看看下面的C#源代碼:
C#線程同步:Monitor
- public static void MyLock()
- {
- lock (typeof(Program))
- {
- }
- }
上面的代碼通過lock語句使MyLock同步,這個(gè)方法被編譯成IL后,代碼如圖1所示。
圖1
從上圖被標(biāo)注的區(qū)域可以看到,一條lock語句被編譯成了調(diào)用Monitor的Enter和Exit方法。Monitor在System.Threading命名空間中。lock的功能就相當(dāng)于直接調(diào)用Monitor的Entry方法,所不同的是,lock方法在結(jié)束后,會(huì)自動(dòng)解除鎖定,當(dāng)然,在IL中是調(diào)用了Monitor的Exit方法,但在C#程序中,看起來是自動(dòng)解鎖的,這類似于C#中的using語句,可以自動(dòng)釋放數(shù)據(jù)庫等的資源。但如果直接在C#源程序中使用Monitor類,就必須調(diào)用Exit方法來顯式地解除鎖定。如下面的代碼所示:
- Monitor.Entry(lockObj);
- try
- {
- // lockObj的同布區(qū)
- }
- catch(Exception e)
- {
- // 異常處理代碼
- }
- finally
- {
- Monitor.Exit(lockObj); // 解除鎖定
- }
Exit方法最后在finally里調(diào)用,這樣無論在方法在發(fā)生異常、返回還是正常執(zhí)行,都會(huì)執(zhí)行到finally,并調(diào)用Exit方法解除鎖定。
Monitor類不僅可以完全取代lock語句(如果只使用lock語句本身的功能,最好還是直接用lock語句吧),還可以使用TryEntry方法設(shè)置一個(gè)鎖定超時(shí),單位是毫秒。如下面的代碼所示:
C#線程同步:Monitor.TryEntry
- if(Monitor.TryEntry(lockObj, 1000))
- {
- try
- {
- }
- finally
- {
- Monitor.Exit(lockObj);
- }
- }
- else
- {
- // 超時(shí)后的處理代碼
- }
上面的代碼設(shè)置了鎖定超時(shí)時(shí)間為1秒,也就是說,在1秒中后,lockObj還未被解鎖,TryEntry方法就會(huì)返回false,如果在1秒之內(nèi),lockObj被解鎖,TryEntry返回true。我們可以使用這種方法來避免死鎖,如下面的代碼所示:
- class Program
- {
- private static Object objA = new Object();
- private static Object objB = new Object();
- public static void LockA()
- {
- if (Monitor.TryEnter(objA, 1000))
- {
- Thread.Sleep(1000);
- if (Monitor.TryEnter(objB, 2000))
- {
- Monitor.Exit(objB);
- }
- else
- {
- Console.WriteLine("LockB timeout");
- }
- Monitor.Exit(objA);
- }
- Console.WriteLine("LockA");
- }
- public static void LockB()
- {
- if (Monitor.TryEnter(objB, 2000))
- {
- Thread.Sleep(2000);
- if (Monitor.TryEnter(objA, 1000))
- {
- Monitor.Exit(objA);
- }
- else
- {
- Console.WriteLine("LockA timeout");
- }
- Monitor.Exit(objB);
- }
- Console.WriteLine("LockB");
- }
- public static void Main()
- {
- Thread threadA = new Thread(LockA);
- Thread threadB = new Thread(LockB);
- threadA.Start();
- threadB.Start();
- Thread.Sleep(4000);
- Console.WriteLine("線程結(jié)束");
- }
- }
上面的代碼是在上一講舉的死鎖的例子,但在這一講將lock語句改成了TryEntry方法,而且設(shè)置了鎖定超時(shí)間,由于在等待一定時(shí)間后,不管被鎖定的對象是否被解鎖,TryEntry方法都會(huì)返回,因此,上面的代碼是不會(huì)死鎖的。運(yùn)行上面的代碼的結(jié)果如圖2所示。
圖2
如果TryEntry方法的超時(shí)時(shí)間為System.Threading.Timeout.Infinite,TryEntry方法就相當(dāng)于Entry方法,如果超時(shí)時(shí)間為0,不管是否解鎖,TryEntry方法都會(huì)立即返回。
以上就是C#線程同步技術(shù)Monitor的使用,希望對大家有所幫助。
【編輯推薦】