近来在学习Eugene Agafonov编写的《C#多线程编程实战》(译),做些笔记也顺便分享一下^-^
using System; using System.Collections.Generic; using System.Threading; namespace ReaderWriterLockSlim_Test { class Program { static void Main(string[] args) { new Thread(Read) { IsBackground = true }.Start(); new Thread(Read) { IsBackground = true }.Start(); new Thread(Read) { IsBackground = true }.Start(); new Thread(() => Write("Thread 1")) { IsBackground = true }.Start(); new Thread(() => Write("Thread 2")) { IsBackground = true }.Start(); Thread.Sleep(TimeSpan.FromSeconds(30)); Console.ReadKey(); } static ReaderWriterLockSlim _rw = new ReaderWriterLockSlim(); static Dictionary<int, int> _items = new Dictionary<int, int>(); static void Read() { Console.WriteLine("Reading contents of a dictionary"); while (true) { try { _rw.EnterReadLock(); foreach (var key in _items.Keys) { Thread.Sleep(TimeSpan.FromSeconds(0.1)); } } finally { _rw.ExitReadLock(); } } } static void Write(string threadName) { while (true) { try { int newKey = new Random().Next(250); _rw.EnterUpgradeableReadLock(); if (!_items.ContainsKey(newKey)) { try { _rw.EnterWriteLock(); _items[newKey] = 1; Console.WriteLine("New key {0} is added to a dictionary by a {1}", newKey, threadName); } finally { _rw.ExitWriteLock(); } } Thread.Sleep(TimeSpan.FromSeconds(0.1)); } finally { _rw.ExitUpgradeableReadLock(); } } } } }
程序运行结果部分结果如下
当主程序启动时,同时运行了三个线程来从字典中读取数据,还有另外两个线程向该字典中写入数据。我们使用ReaderWriterLockSlim类来实现线程安全,该类专为这样的场景而设计。
这里使用两种锁:该锁允许多线程读取数据,写锁在被释放前会阻塞了其他线程的所有操作。获取读锁时还有一个有意思的场景,即从集合中读取数据时,根据当前数据而决定是否获取一个写锁并修改该集合。一旦得到写锁,会阻止阅读者读取数据,从而浪费大量的世界,因此获取写锁后集合会处于阻塞状态。为了最小化阻塞浪费的世界,可以使用EnterUpgradeableReadLock和ExitUpgradeableReadLock方法。先获取读锁后读取数据。如果发现必须修改底层集合,只需使用EnterWriteLock方法升级锁,然后快速执行一次写操作,最后使用ExitWriteLock释放写锁。
在本例中,我们先生成一个随机数。然后获取读锁并检查该数是否存在于字典的键集合中。如果不存在,将读锁更新为写锁然后将该新键加入到字典中。始终使用try/finally代码块来确保在捕获锁后一定会释放锁。