c#多執行緒程式設計筆記4
a)使用Monitor類
Monitor類提供了鎖定部分程式碼的簡單機制,只要把受保護的程式碼包裝在Monitor.Enter與Monitor.Exit程式碼塊中就行了。Monitor.Enter方法與Monitor.Exit方法都有一個引數。
Monitor.Enter(object [obj]);Monitor.Exit(object [obj]).這個引數就是需要Monitor鎖定的物件,它應該是一個引用型別,而不是值型別。
Monitor具有以下功能:(摘自MSDN)
它根據需要與某個物件相關聯。
它是未繫結的,也就是說可以直接從任何上下文呼叫它。
不能建立Monitor類的例項。
例子
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
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):在指定的時間量內嘗試獲取指定物件上的排他鎖
將例子6的increseMC6方法改為如下:
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中斷退出。