ReaderWriterLock測試及自動釋放
在進行多執行緒模式的程式開發時,常常會需要針對會共同存取到的資料來進行同步化的動作。
同步化的方式,最常用到的應該是就Critical Section的方式了。
在C#中,要實作Critical Section的模式的話,可以使用Monitor類別,或是使用C#內建的關鍵字:lock。
不過,使用Critical Section模式的話,表示在任何一個時間下,只有一個執行緒可以來存取這項共用的資源。如果多數的執行緒只是要讀取其中的資料的話,不好意思,大家必項排隊等候這項資源才行。如果有這種情況發生的話,那多執行緒的程式設計方式,將無法為您的程式執行效能帶來太多的改善。因為,每個執行緒還是會需要去等候某項資源。
為了解決這個問題,C#中提供了一個ReaderWriterLock的物件。這種物件的特性就是在同一時間下,可以同時有多個執行緒來讀取某項共用資源。而當有其它的執行緒想要去寫入這項資源時,它也必須先排隊等候這項資源。一旦要寫入的執行緒取得資源的存取權限時,其它需要取得唯讀權限的執行緒,將全部被阻擋在外。要等到執行寫入資源的執行緒完成後,其它唯讀的執行緒才能再度取得資源。
在上面這段程式碼中,首先來看一下類別TestReadWriteLock的部份。在函式StartTest()中,我們利用迴圈的方式,產生了四個用來取得Reader權限的執行緒。而另外,在其後我們產生了一個用來取得Writer權限的執行緒。在執行後,可以確認當還有Reader權限的執行緒在讀取資源時,此時Writer權限不會配置給其它執行緒。只有當所有持有Reader權限的執行緒都釋放出資源時,此時Writer權限才會配置出去。
上面的ReaderWriterLock的使用方式大致上是沒有什麼問題了,只是每次都要用AcquireReaderLock()及ReleaseReaderLock()這樣成對的方式來使用,好像太繁鎖了。所以有人就想到使用using及IDisposable的方式來簡化這樣的步驟。程式碼如類別ReaderLockWrapper所示的。我們可以使用第二份ReaderFunc()中的方式來使用ReaderLockWrapper來實作。
同步化的方式,最常用到的應該是就Critical Section的方式了。
在C#中,要實作Critical Section的模式的話,可以使用Monitor類別,或是使用C#內建的關鍵字:lock。
不過,使用Critical Section模式的話,表示在任何一個時間下,只有一個執行緒可以來存取這項共用的資源。如果多數的執行緒只是要讀取其中的資料的話,不好意思,大家必項排隊等候這項資源才行。如果有這種情況發生的話,那多執行緒的程式設計方式,將無法為您的程式執行效能帶來太多的改善。因為,每個執行緒還是會需要去等候某項資源。
為了解決這個問題,C#中提供了一個ReaderWriterLock的物件。這種物件的特性就是在同一時間下,可以同時有多個執行緒來讀取某項共用資源。而當有其它的執行緒想要去寫入這項資源時,它也必須先排隊等候這項資源。一旦要寫入的執行緒取得資源的存取權限時,其它需要取得唯讀權限的執行緒,將全部被阻擋在外。要等到執行寫入資源的執行緒完成後,其它唯讀的執行緒才能再度取得資源。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace TestVS2008
{
public class TestReadWriteLock
{
protected bool isStop = false;
protected ReaderWriterLock rwLock = new ReaderWriterLock();
public void StartTest()
{
isStop = false;
for (int i = 0; i < 4; i++)
{
Thread t1 = new Thread(new ThreadStart(ReaderFunc));
t1.IsBackground = true;
t1.Start();
}
Thread t2 = new Thread(new ThreadStart(WriterFunc));
t2.IsBackground = true;
t2.Start();
Console.Read();
isStop = true;
}
//protected void ReaderFunc()
//{
// int threadID = Thread.CurrentThread.ManagedThreadId;
// while (!isStop)
// {
// Console.WriteLine("Thread " + threadID.ToString() + " R?");
// rwLock.AcquireReaderLock(Timeout.Infinite);
// Console.WriteLine("Thread " + threadID.ToString() + " R+");
// Thread.Sleep(2000);
// Console.WriteLine("Thread " + threadID.ToString() + " R-");
// rwLock.ReleaseReaderLock();
// }
//}
protected void ReaderFunc()
{
int threadID = Thread.CurrentThread.ManagedThreadId;
while (!isStop)
{
Console.WriteLine("Thread " + threadID.ToString() + " R?");
using (new ReaderLockWrapper(rwLock))
{
Console.WriteLine("Thread " + threadID.ToString() + " R+");
Thread.Sleep(2000);
Console.WriteLine("Thread " + threadID.ToString() + " R-");
}
}
}
protected void WriterFunc()
{
int threadID = Thread.CurrentThread.ManagedThreadId;
while (!isStop)
{
Console.WriteLine("Thread " + threadID.ToString() + " >>>>W?");
rwLock.AcquireWriterLock(Timeout.Infinite);
Console.WriteLine("Thread " + threadID.ToString() + " >>>>W+");
Thread.Sleep(2000);
Console.WriteLine("Thread " + threadID.ToString() + " >>>>W-");
rwLock.ReleaseWriterLock();
}
}
}
public class ReaderLockWrapper : IDisposable
{
protected ReaderWriterLock innerLock = null;
public ReaderLockWrapper(ReaderWriterLock crtLock)
{
innerLock = crtLock;
innerLock.AcquireReaderLock(Timeout.Infinite);
}
#region IDisposable 成員
public void Dispose()
{
int threadID = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine("Thread " + threadID.ToString() + " %%% Disposed");
if (innerLock != null)
{
innerLock.ReleaseReaderLock();
}
}
#endregion
}
}
在上面這段程式碼中,首先來看一下類別TestReadWriteLock的部份。在函式StartTest()中,我們利用迴圈的方式,產生了四個用來取得Reader權限的執行緒。而另外,在其後我們產生了一個用來取得Writer權限的執行緒。在執行後,可以確認當還有Reader權限的執行緒在讀取資源時,此時Writer權限不會配置給其它執行緒。只有當所有持有Reader權限的執行緒都釋放出資源時,此時Writer權限才會配置出去。
上面的ReaderWriterLock的使用方式大致上是沒有什麼問題了,只是每次都要用AcquireReaderLock()及ReleaseReaderLock()這樣成對的方式來使用,好像太繁鎖了。所以有人就想到使用using及IDisposable的方式來簡化這樣的步驟。程式碼如類別ReaderLockWrapper所示的。我們可以使用第二份ReaderFunc()中的方式來使用ReaderLockWrapper來實作。
留言
張貼留言