C#多執行緒程式設計例項

iDotNetSpace發表於2009-02-04
問題的提出

  所謂單個寫入程式/多個閱讀程式的執行緒同步問題,是指任意數量的執行緒訪問共享資源時,寫入程式(執行緒)需要修改共享資源,而閱讀程式(執行緒)需要讀取資料。在這個同步問題中,很容易得到下面二個要求:

  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 );   
}

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/12639172/viewspace-545261/,如需轉載,請註明出處,否則將追究法律責任。

相關文章