本隨筆續接:.NET 同步與異步之鎖(Lock、Monitor)(七)html
因爲鎖 ( lock 和 Monitor ) 是線程獨佔式訪問的,因此其對性能的影響仍是蠻大的,那有沒有一種方式但是實現:容許多個線程同時讀數據、只容許一個線程寫數據呢?答案是確定的。異步
讀寫鎖 ReaderWriterLock 、就是 支持單個寫線程和多個讀線程的鎖。自.NET 3.5 開始 ReaderWriterLockSlim 、登上舞臺,ReaderWriterLockSlim 能夠看作是 ReaderWriterLock 的升級版。 因爲 ReaderWriterLockSlim 默認不支持遞歸調用、因此在某種意義上來講更不容易形成死鎖。ide
1、先看一下demo(來源msdn代碼示例):函數
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(); } } private void ReaderWriterLock() { var sc = new SynchronizedCache(); var tasks = new List<Task>(); int itemsWritten = 0; // Execute a writer. tasks.Add(Task.Run(() => { String[] vegetables = { "broccoli", "cauliflower", "carrot", "sorrel", "baby turnip", "beet", "brussel sprout", "cabbage", "plantain", "spinach", "grape leaves", "lime leaves", "corn", "radish", "cucumber", "raddichio", "lima beans" }; for (int ctr = 1; ctr <= vegetables.Length; ctr++) sc.Add(ctr, vegetables[ctr - 1]); itemsWritten = vegetables.Length; base.PrintInfo(string.Format("Task {0} wrote {1} items\n", Task.CurrentId, itemsWritten)); })); // Execute two readers, one to read from first to last and the second from last to first. for (int ctr = 0; ctr <= 1; ctr++) { bool desc = Convert.ToBoolean(ctr); tasks.Add(Task.Run(() => { int start, last, step; int items; do { String output = String.Empty; items = sc.Count; if (!desc) { start = 1; step = 1; last = items; } else { start = items; step = -1; last = 1; } for (int index = start; desc ? index >= last : index <= last; index += step) output += String.Format("[{0}] ", sc.Read(index)); base.PrintInfo(string.Format("Task {0} read {1} items: {2}\n", Task.CurrentId, items, output)); } while (items < itemsWritten | itemsWritten == 0); })); } // Execute a red/update task. tasks.Add(Task.Run(() => { Thread.Sleep(100); for (int ctr = 1; ctr <= sc.Count; ctr++) { String value = sc.Read(ctr); if (value == "cucumber") if (sc.AddOrUpdate(ctr, "green bean") != SynchronizedCache.AddOrUpdateStatus.Unchanged) base.PrintInfo("Changed 'cucumber' to 'green bean'"); } })); // Wait for all three tasks to complete. Task.WaitAll(tasks.ToArray()); // Display the final contents of the cache. base.PrintInfo(""); base.PrintInfo("Values in synchronized cache: "); for (int ctr = 1; ctr <= sc.Count; ctr++) base.PrintInfo(string.Format(" {0}: {1}", ctr, sc.Read(ctr))); }
2、經過Demo咱們來看一下 ReaderWriterLockSlim 的用法:post
一、EnterWriteLock 進入寫模式鎖定狀態性能
二、EnterReadLock 進入讀模式鎖定狀態ui
三、EnterUpgradeableReadLock 進入可升級的讀模式鎖定狀態url
而且三種鎖定模式都有超時機制、對應 Try... 方法,退出相應的模式則使用 Exit... 方法,並且全部的方法都必須是成對出現的。spa
3、備註及注意事項線程
一、對於同一把鎖、多個線程可同時進入 讀模式。
二、對於同一把鎖、同時只容許一個線程進入 寫模式。
三、對於同一把鎖、同時只容許一個線程進入 可升級的讀模式。
四、經過默認構造函數建立的讀寫鎖是不支持遞歸的,若想支持遞歸 可經過構造 ReaderWriterLockSlim(LockRecursionPolicy) 建立實例。
五、對於同一把鎖、同一線程不可兩次進入同一鎖狀態(開啓遞歸後能夠)
六、對於同一把鎖、即使開啓了遞歸、也不能夠在進入讀模式後再次進入寫模式或者可升級的讀模式(在這以前必須退出讀模式)。
七、再次強調、不建議啓用遞歸。
八、讀寫鎖具備線程關聯性,即 兩個線程間擁有的鎖的狀態 相互獨立不受影響、而且不能相互修改其鎖的狀態。
九、升級狀態:在進入可升級的讀模式 EnterUpgradeableReadLock 後,可在恰當時間點 經過 EnterWriteLock 進入寫模式。
十、降級狀態:可升級的讀模式能夠降級爲讀模式:即 在進入可升級的讀模式 EnterUpgradeableReadLock 後, 經過首先調用讀取模式 EnterReadLock 方法,而後再調用 ExitUpgradeableReadLock 方法。
隨筆暫告一段落、下一篇隨筆介紹:輕量級的鎖(Interlocked、SpinLock)(預計1篇隨筆)
附,Demo : http://files.cnblogs.com/files/08shiyan/ParallelDemo.zip
參見更多:隨筆導讀:同步與異步
(未完待續...)