C#多執行緒程式設計例項
所謂單個寫入程式/多個閱讀程式的執行緒同步問題,是指任意數量的執行緒訪問共享資源時,寫入程式(執行緒)需要修改共享資源,而閱讀程式(執行緒)需要讀取資料。在這個同步問題中,很容易得到下面二個要求:
1) 當一個執行緒正在寫入資料時,其他執行緒不能寫,也不能讀。
2) 當一個執行緒正在讀入資料時,其他執行緒不能寫,但能夠讀。
在資料庫應用程式環境中經常遇到這樣的問題。比如說,有n個終端使用者,他們都要同時訪問同一個資料庫。其中有m個使用者要將資料存入資料庫,n-m個使用者要讀取資料庫中的記錄。
很顯然,在這個環境中,我們不能讓兩個或兩個以上的使用者同時更新同一條記錄,如果兩個或兩個以上的使用者都試圖同時修改同一記錄,那麼該記錄中的資訊就會被破壞。
我們也不讓一個使用者更新資料庫記錄的同時,讓另一使用者讀取記錄的內容。因為讀取的記錄很有可能同時包含了更新和沒有更新的資訊,也就是說這條記錄是無效的記錄。
實現分析
規定任一執行緒要對資源進行寫或讀操作前必須申請鎖。根據操作的不同,分為閱讀鎖和寫入鎖,操作完成之後應釋放相應的鎖。將單個寫入程式/多個閱讀程式的要求改變一下,可以得到如下的形式:
一個執行緒申請閱讀鎖的成功條件是:當前沒有活動的寫入執行緒。
一個執行緒申請寫入鎖的成功條件是:當前沒有任何活動(對鎖而言)的執行緒。
因此,為了標誌是否有活動的執行緒,以及是寫入還是閱讀執行緒,引入一個變數m_nActive,如果m_nActive > 0,則表示當前活動閱讀執行緒的數目,如果m_nActive=0,則表示沒有任何活動執行緒,m_nActive <0,表示當前有寫入執行緒在活動,注意m_nActive<0,時只能取-1的值,因為只允許有一個寫入執行緒活動。
為了判斷當前活動執行緒擁有的鎖的型別,我們採用了執行緒區域性儲存技術(請參閱其它參考書籍),將執行緒與特殊標誌位關聯起來。
申請閱讀鎖的函式原型為:public void AcquireReaderLock( int millisecondsTimeout ),其中的引數為執行緒等待排程的時間。函式定義如下:
public void AcquireReaderLock( int millisecondsTimeout )
{
// m_mutext很快可以得到,以便進入臨界區
m_mutex.WaitOne( );
// 是否有寫入執行緒存在
bool bExistingWriter = ( m_nActive < 0 );
if( bExistingWriter )
{ //等待閱讀執行緒數目加1,當有鎖釋放時,根據此數目來排程執行緒
m_nWaitingReaders++;
} else
{ //當前活動執行緒加1
m_nActive++;
}
m_mutex.ReleaseMutex();
//儲存鎖標誌為Reader
System.LocalDataStoreSlot slot = Thread.GetNamedDataSlot(m_strThreadSlotName);
object bj = Thread.GetData( slot );
LockFlags flag = LockFlags.None;
if( obj != null )
flag = (LockFlags)obj ;
if( flag == LockFlags.None )
{
Thread.SetData( slot, LockFlags.Reader );
}
else
{
Thread.SetData( slot, (LockFlags)((int)flag | (int)LockFlags.Reader ) );
}
if( bExistingWriter )
{ //等待指定的時間
this.m_aeReaders.WaitOne( millisecondsTimeout, true );
}
}
它首先進入臨界區(用以在多執行緒環境下保證活動執行緒數目的操作的正確性)判斷當前活動執行緒的數目,如果有寫執行緒(m_nActive<0)存在,則等待指定的時間並且等待的閱讀執行緒數目加1。如果當前活動執行緒是讀執行緒(m_nActive>=0),則可以讓讀執行緒繼續執行。
申請寫入鎖的函式原型為:public void AcquireWriterLock( int millisecondsTimeout ),其中的引數為等待排程的時間。函式定義如下:
public void AcquireWriterLock( int millisecondsTimeout )
{
// m_mutext很快可以得到,以便進入臨界區
m_mutex.WaitOne( );
// 是否有活動執行緒存在
bool bNoActive = m_nActive == 0;
if( !bNoActive )
{ m_nWaitingWriters++;
} else
{
m_nActive--;
}
m_mutex.ReleaseMutex();
//儲存執行緒鎖標誌
System.LocalDataStoreSlot slot = Thread.GetNamedDataSlot( "myReaderWriterLockDataSlot" );
object bj = Thread.GetData( slot );
LockFlags flag = LockFlags.None;
if( obj != null )
flag = (LockFlags)Thread.GetData( slot );
if( flag == LockFlags.None )
{ Thread.SetData( slot, LockFlags.Writer );
} else
{
Thread.SetData( slot, (LockFlags)((int)flag | (int)LockFlags.Writer ) );
}
//如果有活動執行緒,等待指定的時間
if( !bNoActive )
this.m_aeWriters.WaitOne( millisecondsTimeout, true );
}
它首先進入臨界區判斷當前活動執行緒的數目,如果當前有活動執行緒存在,不管是寫執行緒還是讀執行緒(m_nActive),執行緒將等待指定的時間並且等待的寫入執行緒數目加1,否則執行緒擁有寫的許可權。
釋放閱讀鎖的函式原型為:public void ReleaseReaderLock()。函式定義如下:
public void ReleaseReaderLock()
{
System.LocalDataStoreSlot slot = Thread.GetNamedDataSlot(m_strThreadSlotName );
LockFlags flag = (LockFlags)Thread.GetData( slot );
if( flag == LockFlags.None )
{ return;
}
bool bReader = true; switch( flag )
{
case LockFlags.None:
break;
case LockFlags.Writer:
bReader = false;
break;
}
if( !bReader )
return;
Thread.SetData( slot, LockFlags.None );
m_mutex.WaitOne();
AutoResetEvent autoresetevent = null;
this.m_nActive --;
if( this.m_nActive == 0 )
{ if( this.m_nWaitingReaders > 0 )
{
m_nActive ++ ;
m_nWaitingReaders --;
autoresetevent = this.m_aeReaders;
}
else if( this.m_nWaitingWriters > 0)
{
m_nWaitingWriters--;
m_nActive --;
autoresetevent = this.m_aeWriters ;
}
}
m_mutex.ReleaseMutex();
if( autoresetevent != null )
autoresetevent.Set();
}
釋放閱讀鎖時,首先判斷當前執行緒是否擁有閱讀鎖(通過執行緒區域性儲存的標誌),然後判斷是否有等待的閱讀執行緒,如果有,先將當前活動執行緒加1,等待閱讀執行緒數目減1,然後置事件為有訊號。如果沒有等待的閱讀執行緒,判斷是否有等待的寫入執行緒,如果有則活動執行緒數目減1,等待的寫入執行緒數目減1。釋放寫入鎖與釋放閱讀鎖的過程基本一致,可以參看原始碼。
注意在程式中,釋放鎖時,只會喚醒一個閱讀程式,這是因為使用AutoResetEvent的原歷,讀者可自行將其改成ManualResetEvent,同時喚醒多個閱讀程式,此時應令m_nActive等於整個等待的閱讀執行緒數目。
測試
測試程式取自.Net FrameSDK中的一個例子,只是稍做修改。測試程式如下,
using System;
using System.Threading;
using MyThreading;
class Resource {
myReaderWriterLock rwl = new myReaderWriterLock();
public void Read(Int32 threadNum) {
rwl.AcquireReaderLock(Timeout.Infinite);
try {
Console.WriteLine("Start Resource reading (Thread={0})", threadNum);
Thread.Sleep(250);
Console.WriteLine("Stop Resource reading (Thread={0})", threadNum);
}
finally { rwl.ReleaseReaderLock();
}
}
public void Write(Int32 threadNum) {
rwl.AcquireWriterLock(Timeout.Infinite);
try {
Console.WriteLine("Start Resource writing (Thread={0})", threadNum);
Thread.Sleep(750);
Console.WriteLine("Stop Resource writing (Thread={0})", threadNum);
}
finally { rwl.ReleaseWriterLock();
}
}
}
class App {
static Int32 numAsyncOps = 20;
static AutoResetEvent asyncOpsAreDone = new AutoResetEvent(false);
static Resource res = new Resource();
public static void Main() {
for (Int32 threadNum = 0; threadNum < 20; threadNum++) {
ThreadPool.QueueUserWorkItem(new WaitCallback(UpdateResource), threadNum);
}
asyncOpsAreDone.WaitOne();
Console.WriteLine("All operations have completed.");
Console.ReadLine();
}
// The callback method's signature MUST match that of a System.Threading.TimerCallback
// delegate (it takes an Object parameter and returns void)
static void UpdateResource(Object state) {
Int32 threadNum = (Int32) state;
if ((threadNum % 2) != 0) res.Read(threadNum);
else res.Write(threadNum);
if (Interlocked.Decrement(ref numAsyncOps) == 0)
asyncOpsAreDone.Set();
}
}
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/12639172/viewspace-545261/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- C#多執行緒程式設計C#執行緒程式設計
- C#並行,多執行緒程式設計並行集合和PLINQ的例項講解並行執行緒程式設計
- C#多執行緒程式設計實戰1.1建立執行緒C#執行緒程式設計
- C#多執行緒程式設計(1):執行緒的啟動C#執行緒程式設計
- C#多執行緒程式設計-基元執行緒同步構造C#執行緒程式設計
- java多執行緒例項Java執行緒
- 多執行緒程式設計執行緒程式設計
- 深入淺出Win32多執行緒程式設計--之綜合例項Win32執行緒程式設計
- JavaScript多執行緒程式設計JavaScript執行緒程式設計
- Boost多執行緒程式設計執行緒程式設計
- UNIX多執行緒程式設計執行緒程式設計
- 多執行緒程式設計(轉)執行緒程式設計
- .NET多執行緒程式設計(1):多工和多執行緒 (轉)執行緒程式設計
- 使用執行緒池優化多執行緒程式設計執行緒優化程式設計
- iOS多執行緒程式設計:執行緒同步總結iOS執行緒程式設計
- .NET多執行緒程式設計(3):執行緒同步 (轉)執行緒程式設計
- 5天玩轉C#並行和多執行緒程式設計 —— 第五天 多執行緒程式設計大總結C#並行執行緒程式設計
- Java多執行緒程式設計筆記10:單例模式Java執行緒程式設計筆記單例模式
- [短文速讀 -5] 多執行緒程式設計引子:程式、執行緒、執行緒安全執行緒程式設計
- 執行緒同步(C#程式設計指南)執行緒C#程式設計
- python 多執行緒程式設計Python執行緒程式設計
- Python多執行緒程式設計Python執行緒程式設計
- GCD 與多執行緒程式設計GC執行緒程式設計
- IOS多執行緒程式設計:概述iOS執行緒程式設計
- linux 多執行緒程式設計Linux執行緒程式設計
- 多執行緒程式設計ExecutorService用法執行緒程式設計
- 29. 多執行緒程式設計執行緒程式設計
- .NET多執行緒程式設計(4):執行緒池和非同步程式設計 (轉)執行緒程式設計非同步
- 程式設計思想之多執行緒與多程式(3):Java 中的多執行緒程式設計執行緒Java
- 多執行緒程式設計,處理多執行緒的併發問題(執行緒池)執行緒程式設計
- pytest(13)-多執行緒、多程式執行用例執行緒
- 多執行緒程式設計基礎(一)-- 執行緒的使用執行緒程式設計
- Qt中的多執行緒與執行緒池淺析+例項QT執行緒
- 多執行緒程式設計總結:一、認識多執行緒本質執行緒程式設計
- 程式設計思想之多執行緒與多程式(4):C++ 中的多執行緒程式設計執行緒C++
- C# 多執行緒C#執行緒
- [02] 多執行緒邏輯程式設計執行緒程式設計
- Java多執行緒程式設計基礎Java執行緒程式設計