線程安全的一個(gè)很經(jīng)常的需求是允許并發(fā)讀,但是不允許并發(fā)寫,例如對(duì)于文件就是這樣的。
ReaderWriterLockSlim 在.net framework 3.5的時(shí)候就提供了,它是用來代替以前的”fat”版本的”ReaderWriterLock”
這兩個(gè)類,有兩種基本的鎖----一個(gè)讀鎖,一個(gè)寫鎖。
寫鎖是一個(gè)完全排他鎖。
讀鎖可以和其他的讀鎖兼容
因此當(dāng)一個(gè)線程持有寫鎖的是很,所有的嘗試獲取讀鎖和寫鎖的線程全部阻塞,但是如果沒有一個(gè)線程持有寫鎖,那么可以有一系列的線程并發(fā)的獲取讀鎖。
ReaderWriterLockSlim 定義了下面幾個(gè)方法來獲取和釋放 讀寫鎖。
Public void EnterReadLock();
Public void ExitReadLock();
Public void EnterWriteLock();
Public void ExitWriteLock();
和Monitor.TryEnter類似,ReaderWriterLockSlim 再對(duì)應(yīng)的”EnterXXX”方法上也提供了相應(yīng)的”Try”版本。ReaderWriterLock提供了AcquireXXX 和 ReleaseXXX 方法,當(dāng)超時(shí)發(fā)生了,ReaderWriterLock 拋出一個(gè)ApplicationException,而不是返回false。
public static void Main()
{
///三個(gè)讀線程
new Thread(Read).Start();
new Thread(Read).Start();
new Thread(Read).Start();
//兩個(gè)寫線程
new Thread(Write).Start("A");
new Thread(Write).Start("B");
}
static void Read()
{
while (true)
{
_rw.EnterReadLock();//獲取讀鎖
//模擬讀的過程
foreach (int i in _items)
Thread.Sleep(100);
_rw.ExitReadLock();//釋放讀鎖
}
}
static void Write(object threadID)
{
while (true)
{
Console.WriteLine(_rw.CurrentReadCount + " concurrent readers");
int newNumber = GetRandomNum(100);
_rw.EnterWriteLock(); //獲取寫鎖
_items.Add(newNumber); //寫數(shù)據(jù)
_rw.ExitWriteLock(); //釋放寫鎖
Console.WriteLine("Thread " + threadID + " added " + newNumber);
Thread.Sleep(100);
}
}
//獲取隨機(jī)數(shù)
static int GetRandomNum(int max) { lock (_rand) return _rand.Next(max); }
像CurrentReadCount 屬性,ReaderWriterLockSlim 提供了以下屬性用來監(jiān)視鎖。
可更新鎖:
再一個(gè)原子操作里將讀鎖升級(jí)為寫鎖是很有用的,例如,假設(shè)你想要再一個(gè)list 里面寫一些不存在的項(xiàng)的時(shí)候, 你可能會(huì)執(zhí)行下面的一些步驟:
問題是:在第三步和第四步之間,可能有另一個(gè)線程修改了列表。
ReaderWriterLockSlim 通過一個(gè)叫做可更新鎖( upgradeable lock),來解決這個(gè)問題。
一個(gè)可更新鎖除了它可以在一個(gè)原子操作中變成寫鎖外很像一個(gè)讀鎖,你可以這樣使用它:
從調(diào)用者的角度來看,它很像一個(gè)嵌套/遞歸鎖,從功能上講,在第三步,
ReaderWriterLockSlim 在一個(gè)原子操作里面釋放讀鎖,然后獲取寫鎖。
可更新鎖和讀鎖的重要區(qū)別是:盡管可更新鎖可以和讀鎖共存,但是一次只能有一個(gè)可更新鎖被獲取。這樣的主要目的是防止死鎖。
這樣我們可以修改Write方法,讓它可以添加一些不在列表中的Item。 int newNumber = GetRandomNum(100); _rw.EnterUpgradeableReadLock(); //獲取可更新鎖 Thread.Sleep(100);
static void Write(object threadID)
{
while (true)
{
Console.WriteLine(_rw.CurrentReadCount + " concurrent readers");
if (!_items.Contains(newNumber)) //如果要寫的東西不在列表中
{
_rw.EnterWriteLock(); //可更新鎖變成寫鎖
_items.Add(newNumber); //寫東西
_rw.ExitWriteLock(); //重新變回可更新鎖
Console.WriteLine("Thread " + threadID + " added " + newNumber); //讀數(shù)據(jù)
}
_rw.ExitUpgradeableReadLock(); //退出可更新鎖
}
}
從上面的例子可以看到C#提供的讀寫鎖功能強(qiáng)大,使用方便,
所以在自己編寫讀寫鎖的時(shí)候,要考慮下是否需要支持可更新鎖,是否有必要自己寫一個(gè)讀寫鎖.
新聞熱點(diǎn)
疑難解答
圖片精選