看下組織結構:
System.Object
System.MarshalByRefObject
System.Threading.WaitHandle
System.Threading.Mutex
System.Threading.Semaphore
System.Threading.EventWaitHandle
System.Threading.ManualResetEvent
System.Threading.AutoResetEvent
System.Object
System.Threading.Interlocked
System.Threading.Monitor
System.Threading.ReaderWriterLock
1, lock 關鍵字其實就是對 Monitor 類的 Enter()和 Exit()方法的封裝。通過 try......catch......finally 語句塊確保在 lock 語句塊結束後執行 Monitor.Exit()方法,釋放互斥鎖。下面2段代碼等價:
lock(locker) { //do something }View Code
Monitor.Enter(locker); try { // do something } finally { Monitor.Exit(locker); }View Code
2,
Monitor類通過向單個線程授予對象鎖來控制對對象的訪問。對象鎖提供限制訪問臨界區的能力。當一個線程擁有對象的鎖時,其他任何線程都不能獲取該鎖。還可以使用 Monitor 來確保不會允許其他任何線程訪問正在由鎖的所有者執行的應用程序代碼節,除非另一個線程正在使用其他的鎖定對象執行該代碼。通過對 lock 關鍵字的分析我們知道,lock 就是對 Monitor 的 Enter 和 Exit 的一個封裝,而且使用起來更簡潔,因此 Monitor 類的 Enter()和 Exit()方法的組合使用可以用 lock 關鍵字替代。
Monitor 類的常用方法:
TryEnter():
能夠有效的解決長期死等的問題,如果在一個並發經常發生,而且持續時間長的環境中使用 TryEnter,可以有效防止死鎖或者長時間的等待。比如我們可以設置一個等待時間 bool gotLock = Monitor.TryEnter(myobject,1000),讓當前線程在等待 1000 秒後根據返回的 bool 值來決定是否繼續下面的操作。
Wait() :
釋放對象上的鎖以便允許其他線程鎖定和訪問該對象。在其他線程訪問對象時,調用線程將等待。脈沖信號用於通知等待線程有關對象狀態的更改。
Pulse():
PulseAll():
向一個或多個等待線程發送信號。該信號通知等待線程鎖定對象的狀態已更改,並且鎖的所有者准備釋放該鎖。等待線程被放置在對象的就緒隊列中以便它可以最後接收對象鎖。一旦線程擁有了鎖,它就可以檢查對象的新狀態以查看是否達到所需狀態。注意:Pulse、PulseAll 和 Wait 方法必須從同步的代碼塊內調用。
static object locker = new object(); static bool isHave = false; static void Produce() { lock (locker) { while (true) { //如果已有產品,則等待消費完成 if (isHave) Monitor.Wait(locker); Console.WriteLine("生產一個"); Thread.Sleep(1000); isHave = true; Monitor.Pulse(locker); } } } static void Consume() { lock (locker) { while (true) { //如果沒有產品,則等待生產完成 if (!isHave) Monitor.Wait(locker); Console.WriteLine("消費一個"); Thread.Sleep(500); isHave = false; Monitor.Pulse(locker); } } }View Code
在main函數中調用:
new Thread(Produce).Start(); new Thread(Consume).Start();View Code
3, Mutex互斥體
public class Test { // Create a new Mutex. The creating thread does not own the // Mutex. private static Mutex mut = new Mutex(); public static void MyThreadProc() { for (int i = 0; i < 2; i++) { UseResource(); } } // This method represents a resource that must be synchronized // so that only one thread at a time can enter. private static void UseResource() { // Wait until it is safe to enter. mut.WaitOne(); Console.WriteLine("{0} has entered the protected area", Thread.CurrentThread.Name); // Place code to access non-reentrant resources here. // Simulate some work. Thread.Sleep(500); Console.WriteLine("{0} is leaving the protected area\r\n", Thread.CurrentThread.Name); // Release the Mutex. mut.ReleaseMutex(); } }View Code
Test test = new Test(); for (int i = 0; i < 3; i++) { Thread myThread = new Thread(new ThreadStart(Test.MyThreadProc)); myThread.Name = String.Format("Thread{0}", i + 1); myThread.Start(); }View Code
mutex還可以判斷系統是否已經有一個進程存在。
4,Semaphore 信號量
static Semaphore sph = new Semaphore(0, 3); static void TProc() { while (true) { if (sph.WaitOne(500, false)) { try { Console.WriteLine("thread" + Thread.CurrentThread.Name + ":enter"); Thread.Sleep(1000); } finally { sph.Release(); Console.WriteLine("thread" + Thread.CurrentThread.Name + ":exit"); } } else { Console.WriteLine("thread" + Thread.CurrentThread.Name + ":time out"); } } }View Code
Thread t = null; for (int i = 0; i < 2; i++) { t = new Thread(TProc); t.Name = i.ToString(); t.Start(); } Console.WriteLine("main sleep 4s"); Thread.Sleep(4000); sph.Release(2);View Code
5,Interlocker類為多個線程共享的變量提供原子操作,它是一個靜態類,主要的成員方法如下:
Add:以原子操作的形式,添加兩個整數並用兩者的和替換第一個整數。
Exchange:以原子操作的形式將變量設置為指定的值,並返回先前值
CompareExchange:比較兩個值是否相等,如果相等,則替換其中一個值
Equals:確定兩個Object 實例是否相等
Increment:以原子操作的形式遞增指定變量的值並存儲結果
Decrement:以原子操作的形式遞減指定變量的值並存儲結果
Read:返回一個以原子操作形式加載的 64 位值
Interlocked.CompareExchange(ref obj, new object(), null);View Code
6, ReaderWriterLock
static ReaderWriterLock rwLock = new ReaderWriterLock(); static object locker = new object(); static void Main(string[] args) { Thread t = null; for(int i = 0; i < 2;i++) { t = newThread(Writer); t.Name =i.ToString(); t.Start(); } for(int i = 0; i<3;i++) { t = newThread(Reader); t.Name =i.ToString(); t.Start(); } Console.ReadLine(); } static void Writer() { while(true) { try { rwLock.AcquireWriterLock(3000); Console.WriteLine("writer:"+ Thread.CurrentThread.Name + " is enter" + "WriterSeqNum:" +rwLock.WriterSeqNum.ToString()); try { Thread.Sleep(5000); } finally { rwLock.ReleaseWriterLock(); Console.WriteLine("writer:"+ Thread.CurrentThread.Name + " is exit"); } } catch(ApplicationException) { Console.WriteLine("writer:"+ Thread.CurrentThread.Name + " wait time out"); } } } static void Reader() { while (true) { rwLock.AcquireReaderLock(-1); Console.WriteLine("reader:"+ Thread.CurrentThread.Name + " is enter" + "WriterSeqNum:" +rwLock.WriterSeqNum.ToString()); try { Thread.Sleep(3000); } finally { Console.WriteLine("reader:"+ Thread.CurrentThread.Name + " is exit"); rwLock.ReleaseReaderLock(); } } }View Code
7,AutoResetEvent 自動重置事件
在構造事件對象時需要指定initialState參數是True還是False,以指示事件的初始狀態是有信號還是無信號
當一個自動重置事件得到信號時,等待該事件的線程中只有一個線程變為可調度線程,當手動重置對象得到信號時,等待該事件的所有線程均變為可調度線程
class Example { private static AutoResetEvent event_1 = new AutoResetEvent(true); private static AutoResetEvent event_2 = new AutoResetEvent(false); static void Main() { Console.WriteLine("Press Enter to create three threads and start them.\r\n" + "The threads wait on AutoResetEvent #1, which was created\r\n" + "in the signaled state, so the first thread is released.\r\n" + "This puts AutoResetEvent #1 into the unsignaled state."); Console.ReadLine(); for (int i = 1; i < 4; i++) { Thread t = new Thread(ThreadProc); t.Name = "Thread_" + i; t.Start(); } Thread.Sleep(250); for (int i = 0; i < 2; i++) { Console.WriteLine("Press Enter to release another thread."); Console.ReadLine(); event_1.Set(); Thread.Sleep(250); } Console.WriteLine("\r\nAll threads are now waiting on AutoResetEvent #2."); for (int i = 0; i < 3; i++) { Console.WriteLine("Press Enter to release a thread."); Console.ReadLine(); event_2.Set(); Thread.Sleep(250); } // Visual Studio: Uncomment the following line. //Console.Readline(); } static void ThreadProc() { string name = Thread.CurrentThread.Name; Console.WriteLine("{0} waits on AutoResetEvent #1.", name); event_1.WaitOne(); Console.WriteLine("{0} is released from AutoResetEvent #1.", name); Console.WriteLine("{0} waits on AutoResetEvent #2.", name); event_2.WaitOne(); Console.WriteLine("{0} is released from AutoResetEvent #2.", name); Console.WriteLine("{0} ends.", name); } }View Code
8, ManualResetEvent 手動重置事件
public class Example { // mre is used to block and release threads manually. It is // created in the unsignaled state. private static ManualResetEvent mre = new ManualResetEvent(false); static void Main() { Console.WriteLine("\nStart 3 named threads that block on a ManualResetEvent:\n"); for(int i = 0; i <= 2; i++) { Thread t = new Thread(ThreadProc); t.Name = "Thread_" + i; t.Start(); } Thread.Sleep(500); Console.WriteLine("\nWhen all three threads have started, press Enter to call Set()" + "\nto release all the threads.\n"); Console.ReadLine(); mre.Set(); Thread.Sleep(500); Console.WriteLine("\nWhen a ManualResetEvent is signaled, threads that call WaitOne()" + "\ndo not block. Press Enter to show this.\n"); Console.ReadLine(); for(int i = 3; i <= 4; i++) { Thread t = new Thread(ThreadProc); t.Name = "Thread_" + i; t.Start(); } Thread.Sleep(500); Console.WriteLine("\nPress Enter to call Reset(), so that threads once again block" + "\nwhen they call WaitOne().\n"); Console.ReadLine(); mre.Reset(); // Start a thread that waits on the ManualResetEvent. Thread t5 = new Thread(ThreadProc); t5.Name = "Thread_5"; t5.Start(); Thread.Sleep(500); Console.WriteLine("\nPress Enter to call Set() and conclude the demo."); Console.ReadLine(); mre.Set(); // If you run this example in Visual Studio, uncomment the following line: //Console.ReadLine(); } private static void ThreadProc() { string name = Thread.CurrentThread.Name; Console.WriteLine(name + " starts and calls mre.WaitOne()"); mre.WaitOne(); Console.WriteLine(name + " ends."); } }View Code
9, .NET 在一些集合類,如 Queue、ArrayList、HashTable 和 Stack,已經提供了一個供 lock 使用的對象 SyncRoot。
Queue q = new Queue(); lock (q.SyncRoot) { foreach (object item in q) { //do something } }View Code
參考資料:http://blog.csdn.net/zzy7075/article/details/29842165