一、前言 本文章彙總c#中常見的鎖,基本都列出了該鎖在微軟官網的文章,一些不常用的鎖也可以參考微軟文章左側的列表,方便溫習回顧。 二、鎖的分類 2.1、用戶模式鎖 1、volatile 關鍵字 volatile 並沒有實現真正的線程同步,操作級別停留在變數級別並非原子級別,對於單系統處理器中,變數存 ...
一、前言
本文章彙總c#中常見的鎖,基本都列出了該鎖在微軟官網的文章,一些不常用的鎖也可以參考微軟文章左側的列表,方便溫習回顧。
二、鎖的分類
2.1、用戶模式鎖
1、volatile 關鍵字
volatile 並沒有實現真正的線程同步,操作級別停留在變數級別並非原子級別,對於單系統處理器中,變數存儲在主記憶體中,沒有機會被別人修改。但是如果是多處理器,可能就會有問題,因為每個處理器都有單獨的data cache,數據更新不一定立刻被寫回到主存,可能會造成不同步。
參考:valatile 微軟官網文章。
2、Spinlock 旋轉鎖
Spinlock 是內核中提供的一種比較常見的鎖機制,自旋鎖是“原地等待”的方式解決資源衝突的,即,一個線程獲取了一個自旋鎖後,另外一個線程期望獲取該自旋鎖則獲取不到,只能夠原地“打轉”(忙等待)。由於自旋鎖的這個忙等待的特性,註定了它使用場景上的限制 :自旋鎖不應該被長時間的持有(消耗 CPU 資源)。
參考:Spinlock 微軟官網文章。
2.2、內核模式鎖
1、事件鎖
自動事件鎖:AutoResetEvent
WaitOne()進入等待,Set()會釋放當前鎖給一個等待線程。
var are = new AutoResetEvent(true); are.WaitOne(); //... are.Set();
手動事件鎖:ManualResetEvent
WaitOne()進入等待,Set()會釋放當前鎖給所有等待線程。
var mre = new ManualResetEvent(false); mre.WaitOne();//批量攔截,後續的省略號部分是無序執行的。 //... mre.Set();//一次釋放給所有等待線程
參考:ManuaResetEvent 微軟官網文章。
2、信號量
信號量:Semaphore
信號量可以控制同時通過的線程數以及總的線程數。
//第一個參數表示同時可以允許的線程數,比如1表示每次只允許一個線程通過, //第二個是最大值,比如8表示最多有8個線程。 var semaphore = new Semaphore(1, 8);
參考:Semaphore 微軟官網文章。
3、互斥鎖
互斥鎖:Mutex
Mutex和Monitor很接近,但是沒有Monitor.Pulse,Wait,PulseAll的喚醒功能,他的優點是可以跨進程,可以在同一臺機器甚至遠程機器人的不同進程間共用一個互斥體。
var mutex = new Mutex(); mutex.WaitOne(); //... mutex.ReleaseMutex();
參考:Mutex 微軟官網文章。
4、讀寫鎖
讀寫鎖:ReaderWriterLock
不要使用ReaderWriterLock,該類有問題(死鎖、性能),請使用ReaderWriterLockSlim
.NET Framework有兩個讀取器-編寫器鎖,ReaderWriterLockSlim以及ReaderWriterLock。 建議對所有新開發的項目使用 ReaderWriterLockSlim。 雖然 ReaderWriterLockSlim 類似於 ReaderWriterLock,但不同之處在於,前者簡化了遞歸規則以及鎖狀態的升級和降級規則。 ReaderWriterLockSlim 避免了許多潛在的死鎖情況。 另外,ReaderWriterLockSlim 的性能顯著優於 ReaderWriterLock。
參考:ReaderWriterLock 微軟官網文章。
讀寫鎖:ReaderWriterLockSlim
//源碼摘錄自微軟官網 using System; using System.Threading; using System.Threading.Tasks; using System.Collections.Generic; public class SynchronizedCache { private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim(); private Dictionary<int, string> innerCache = new Dictionary<int, string>(); public int Count { get { return innerCache.Count; } } public string Read(int key) { cacheLock.EnterReadLock(); try { return innerCache[key]; } finally { cacheLock.ExitReadLock(); } } public void Add(int key, string value) { cacheLock.EnterWriteLock(); try { innerCache.Add(key, value); } finally { cacheLock.ExitWriteLock(); } } public bool AddWithTimeout(int key, string value, int timeout) { if (cacheLock.TryEnterWriteLock(timeout)) { try { innerCache.Add(key, value); } finally { cacheLock.ExitWriteLock(); } return true; } else { return false; } } public AddOrUpdateStatus AddOrUpdate(int key, string value) { cacheLock.EnterUpgradeableReadLock(); try { string result = null; if (innerCache.TryGetValue(key, out result)) { if (result == value) { return AddOrUpdateStatus.Unchanged; } else { cacheLock.EnterWriteLock(); try { innerCache[key] = value; } finally { cacheLock.ExitWriteLock(); } return AddOrUpdateStatus.Updated; } } else { cacheLock.EnterWriteLock(); try { innerCache.Add(key, value); } finally { cacheLock.ExitWriteLock(); } return AddOrUpdateStatus.Added; } } finally { cacheLock.ExitUpgradeableReadLock(); } } public void Delete(int key) { cacheLock.EnterWriteLock(); try { innerCache.Remove(key); } finally { cacheLock.ExitWriteLock(); } } public enum AddOrUpdateStatus { Added, Updated, Unchanged }; ~SynchronizedCache() { if (cacheLock != null) cacheLock.Dispose(); } }ReaderWriterLockSlim示例
參考:ReaderWriterLockSlim 微軟官網文章。
2.3、動態計數鎖
1、動態計數鎖:CountdownEvent
限制線程數的一個機制,而且這個也是比較常用的(同屬於信號量的一種)。
var cde = new CountdownEvent(10); //重置當前ThreadCount上限 cde.Reset(10); for(int i=0; i<10; i++) { Task.Factory.StartNew(()=> { Thread.Sleep(1000); SubWoker1(); }); } cde.Wait();//相當於Task.WaitAll() cde.Reset(8); for(int i=0; i<8; i++) { Task.Factory.StartNew(()=> { Thread.Sleep(1000); SubWoker2(); }); } cde.Wait();//相當於Task.WaitAll() static void SubWoker1() { //... cde.Signal();//將當前的ThreadCount-1操作。 } static void SubWoker2() { //... cde.Signal();//將當前的ThreadCount-1操作。 }CountdownEvent示例
參考:CountdownEvent 微軟官網文章。
2、原子操作類:Interlocked
Interlocked類則提供了4種方法進行原子級別的變數操作。Increment , Decrement , Exchange 和CompareExchange 。
a、使用Increment 和Decrement 可以保證對一個整數的加減為一個原子操作。
b、Exchange 方法自動交換指定變數的值。
c、CompareExchange 方法組合了兩個操作:比較兩個值以及根據比較的結果將第三個值存儲在其中一個變數中。
d、比較和交換操作也是按原子操作執行的。Interlocked.CompareExchange(ref a, b, c); 原子操作,a參數和c參數比較, 相等b替換a,不相等不替換。
參考:Interlocked 微軟官網文章。
2.4、監視鎖
1、監視鎖:Monitor
Monitor鎖為操作的代碼塊添加互斥對象,如果A線程正在訪問,對象沒有到達臨界區,則B線程不會訪問。
參考:Monitor 微軟官網文章。
2、監視鎖:lock
lock鎖可以視為monitor鎖的語法糖,增加了自動釋放機制和異常處理機制。
a、不推薦使用lock(this)的方式作為lock鎖,因為你不確定別的地方是否重新實例了含有lock的對象。
b、不要lock一個字元串。
c、不要lock一個外部公開變數。