標籤:c# 多線程 使用
C#中, 通過System.Threading.Monitor類可以實現多線程中對某些代碼塊的同步訪問,以確保資料的安全性。
object obj=new object();
Monitor在鎖對象obj上會維持兩個線程隊列R和W以及一個引用T :
(1) T是對當前獲得了obj鎖的線程的引用(設此線程為CurrThread);
(2) R為就緒隊列,其上的線程已經準備好擷取obj鎖。當obj鎖被CurrThread釋放後(CurrThread可通過Monitor.Exit(obj)或Monitor.Wait(obj)來釋放其所獲的obj鎖)這些線程就會去競爭obj鎖,獲得obj鎖的線程將被T引用; 線程調用Monitor.Enter(obj)或Monitor.TryEnter(obj)將會使該線程直接進入R隊列。
(3) W為等待隊列,其上的線程是因為調用了Monitor.Wait(obj)而進入W隊列的;W上的線程不會被OS直接調度執行,也就是說它們沒有準備好擷取obj鎖,就是說在等待隊列上的線程不能去獲得obj鎖。當前獲得obj鎖的線程CurrThread調用Monitor.Pulse(obj)或Monitor.PulseAll(obj)後會使W隊列中的第一個等待線程或所有等待線程被移至R隊列,這時被移至R隊列的這些線程就有機會被OS直接調度執行,也就是有可以去競爭obj鎖。
Monitor類中的重要方法:
方法名稱
|
描述
|
void Enter(object obj) |
Acquires an exclusive lock on the specified object. |
void Enter(object obj, ref bool lockTaken) |
Acquires an exclusive lock on the specified object, and atomically sets a value that indicates whether the lock was taken. |
void Exit(object obj) |
Releases an exclusive lock on the specified object. |
void Pulse(object obj) |
Notifies a thread in the waiting queue of a change in the locked object‘s state. |
void PulseAll(object obj) |
Notifies all waiting threads of a change in the object‘s state. |
bool TryEnter(object obj) |
Attempts to acquire an exclusive lock on the specified object. |
bool TryEnter(object obj, int millisecondsTimeout) |
Attempts, for the specified number of milliseconds, to acquire an exclusive lock on the specified object. |
bool Wait(object obj) |
Releases the lock on an object and blocks the current thread until it reacquires the lock. If the specified time-out interval elapses, the thread enters the ready queue. |
例子1:
private object lockObj = new object(); private void Execute() { Monitor.Enter(lockObj); for (int i = 0; i < 5;i++ ) { Console.WriteLine("Thread Name:" + Thread.CurrentThread.Name + ", Count:" + i); Thread.Sleep(new Random().Next(5000)); } Monitor.Exit(lockObj); } public void Test() { Thread thread1 = new Thread(new ThreadStart(Execute)); thread1.Name = "Thread1"; thread1.Start(); Thread thread2 = new Thread(new ThreadStart(Execute)); thread2.Name = "Thread2"; thread2.Start(); thread1.Join(); thread2.Join(); }
輸出結果:
Thread Name:Thread1, Count:0
Thread Name:Thread1, Count:1
Thread Name:Thread1, Count:2
Thread Name:Thread1, Count:3
Thread Name:Thread1, Count:4
Thread Name:Thread2, Count:0
Thread Name:Thread2, Count:1
Thread Name:Thread2, Count:2
Thread Name:Thread2, Count:3
Thread Name:Thread2, Count:4
例子2:
private void Execute1() { if (Monitor.TryEnter(lockObj,1000)) { for (int i = 0; i < 5; i++) { Console.WriteLine("Thread Name:" + Thread.CurrentThread.Name + ", Count:" + i); Thread.Sleep(new Random().Next(5000)); } Monitor.Exit(lockObj); } } public void Test1() { Thread thread1 = new Thread(new ThreadStart(Execute1)); thread1.Name = "Thread1"; thread1.Start(); Thread thread2 = new Thread(new ThreadStart(Execute1)); thread2.Name = "Thread2"; thread2.Start(); thread1.Join(); thread2.Join(); }
輸出結果:
Thread Name:Thread1, Count:0
Thread Name:Thread1, Count:1
Thread Name:Thread1, Count:2
Thread Name:Thread1, Count:3
Thread Name:Thread1, Count:4
因為Thread2嘗試擷取鎖失敗,所以沒有執行鎖內部的代碼塊。
例子3:
private Queue queue = new Queue(); public void SendThread() { int counter = 0; lock (queue) { while (counter < 5) { //Wait, if the queue is busy. Monitor.Wait(queue); //Push one element. queue.Enqueue(counter); //Release the waiting thread. Monitor.Pulse(queue); counter++; } } } public void ConsumeThread() { lock (queue) { //Release the waiting thread. Monitor.Pulse(queue); //Wait in the loop, while the queue is busy. //Exit on the time-out when the first thread stops. while (Monitor.Wait(queue, 1000)) { //Pop the first element. int counter = (int)queue.Dequeue(); //Print the first element. Console.WriteLine("Queue Value:" + counter.ToString()); //Release the waiting thread. Monitor.Pulse(queue); } } } public void Test2() { Thread thread1 = new Thread(new ThreadStart(SendThread)); thread1.Name = "Thread1"; thread1.Start(); Thread thread2 = new Thread(new ThreadStart(ConsumeThread)); thread2.Name = "Thread2"; thread2.Start(); thread1.Join(); thread2.Join(); Console.WriteLine("Queue Size:" + queue.Count); }
輸出結果:
Queue Value:0
Queue Value:1
Queue Value:2
Queue Value:3
Queue Value:4
Queue Size:0