1. 程式人生 > >c#多執行緒程式設計筆記4

c#多執行緒程式設計筆記4

a)使用Monitor

Monitor類提供了鎖定部分程式碼的簡單機制,只要把受保護的程式碼包裝在Monitor.EnterMonitor.Exit程式碼塊中就行了。Monitor.Enter方法與Monitor.Exit方法都有一個引數。

Monitor.Enter(object [obj]);Monitor.Exit(object [obj]).這個引數就是需要Monitor鎖定的物件,它應該是一個引用型別,而不是值型別。

Monitor具有以下功能:(摘自MSDN

它根據需要與某個物件相關聯。

它是未繫結的,也就是說可以直接從任何上下文呼叫它。

不能建立Monitor類的例項。

例子

6:

using System;

using System.Collections.Generic;

using System.Text;

using System.Threading;

namespace ConsoleApplication1

{

public class MonitorClass6

{

protected static int m_iCounter = 0;

public void Increment()

{

m_iCounter++;

}

public int Counter

{

get

{

return m_iCounter;

}

}

}

public class test

{

public MonitorClass6 mc6 = new MonitorClass6();

public void increseMC6()

{

Monitor.Enter(mc6);//進入臨界區

try

{

mc6.Increment();

Console.WriteLine("執行緒{0}的值為{1}",Thread.CurrentThread.Name,mc6.Counter);

}

finally

{

Monitor.Exit(mc6);//退出臨界區

}

}

}

public class MainEntryPoint

{

public static test myTest = new

test();

public static void Main()

{

ThreadStart ts1 = new ThreadStart(execute);

Thread t1 = new Thread(ts1);

t1.Name = "Thread1";

ThreadStart ts2 = new ThreadStart(execute1);

Thread t2 = new Thread(ts2);

t2.Name = "Thread2";

t1.Start();

t2.Start();

Console.Read();

}

public static void execute()

{

for (int i = 0; i <5; i++)

{

myTest.increseMC6();

}

}

public static void execute1()

{

for (int i = 0; i < 5; i++)

{

myTest.increseMC6();

}

}

}

}

執行結果如下:

可以看到它沒有經過順序上的紊亂就達到10;如果我們把兩個有帶有Monitor註釋掉,結果會變成下圖所示(您機子上的結果也許順序不一樣)

可以看出,由於我們沒有對臨界資源加以控制,而形成了我們不需要的“髒”資料!

也許細心的你會發現這個Monitor類的形式與使用LOCK關鍵字基本一致,但我認為它比lock關鍵字強大一些,因為Monitor類還具有 TryEnter(試圖獲取指定物件的排他鎖)、Wait(釋放物件上的鎖並阻止當前執行緒)、Pulse(能知等待佇列中的執行緒鎖定物件狀態的更改)以及PulseAll(通知所有的等待執行緒的物件狀態已改變)等方法。

lTtyEnter

其方法的過載:

Monitor.TryEnter(Object):試圖獲得指定物件的排他鎖

Monitor.TryEnter(Object,Int32):在指定的毫秒數內嘗試獲取指定物件上的排他鎖

Monitor.TryEnter(Object,TimeSpan):在指定的時間量內嘗試獲取指定物件上的排他鎖

將例子6increseMC6方法改為如下:

public void increseMC6()

{

if (Monitor.TryEnter(mc6))//進入臨界區

{

try

{

mc6.Increment();

Console.WriteLine("執行緒{0}的值為{1}", Thread.CurrentThread.Name, mc6.Counter);

}

finally

{

Monitor.Exit(mc6);//退出臨界區

}

}

else

{

Console.WriteLine("執行緒{0}沒有競得資源",Thread.CurrentThread.Name);

}

}

}

lWait

其方法的過載:

Monitor.Wait(Object):釋放物件上的鎖並阻止當前執行緒,直到它重新獲取該鎖

Monitor.Wait(Object,Int32):釋放物件上的鎖並阻止當前執行緒,直到它重新獲取該鎖。如果指定的超時間隔已過,則執行緒進入就緒佇列。

Monitor.Wait(Object,TimeSpan):同上。

Monitor.Wait(Object,Int32,Boolean):前兩個與第二個一樣。其中的Boolean表示是否在等待之前退出上下文的同步域然後重新獲取該同步域。

Monitor.Wait(Object,TimeSpan,Boolean):同上。

例程7

using System;

using System.Collections.Generic;

using System.Text;

using System.Threading;

namespace ConsoleApplication1

{

public class Monitor7

{

protected int m_iCounter = 0;

public void Increment()

{

m_iCounter++;

}

public int Counter

{

get

{

return m_iCounter;

}

}

}

public class MonitorPulseClass

{

protected Monitor7 m_protectedResource = new Monitor7();

protected void ThreadOneMethod()

{

for (int i = 0; i < 5; i++)

{

lock (m_protectedResource)

{

Console.WriteLine("進入第一個執行緒,I的值為:{0}",i);

Monitor.Wait(m_protectedResource);

m_protectedResource.Increment();

int iValue = m_protectedResource.Counter;

Console.WriteLine("{Thread One} - Current value of counter:" + iValue.ToString());

}

}

}

protected void ThreadTwoMethod()

{

for (int i = 0; i < 5; i++)

{

lock (m_protectedResource)

{

Console.WriteLine("進入第二個執行緒,I的值為:{0}",i);

int iValue = m_protectedResource.Counter;

Console.WriteLine("{Thread Two} - Current value of counter" + iValue.ToString());

Monitor.PulseAll(m_protectedResource);

}

}

}

static void Main()

{

MonitorPulseClass exampleClass = new MonitorPulseClass();

Thread threadOne = new Thread(new ThreadStart(exampleClass.ThreadOneMethod));

Thread threadTwo = new Thread(new ThreadStart(exampleClass.ThreadTwoMethod));

threadOne.Start();

threadTwo.Start();

System.Console.Read();

}

}

}

執行結果如下:由此可以得到流程如下:首先進入thread1然後被Monitor.Wait中斷,從而進入thread2,thread2中結束之後,通過Monitor.PulseAll通知thread1並執行,但又被Monitor.wait中斷退出。