Windows平台下的讀寫鎖

來源:互聯網
上載者:User

Windows平台下的讀寫鎖
簡單介紹Windows平台下的讀寫鎖以及實現.

背景介紹
Windows在Vista 和 Server2008以後才開始提供讀寫鎖API,即SRW系列函數(InitializeSRWLock, AcquireSRWLockShared, AcquireSRWLockExclusive等).考慮到目前Windows XP的裝機量,只能自己實現一個讀寫鎖了.

讀寫鎖的目的和要求
讀寫鎖的最基本目的是讀鎖可以共用,寫鎖必須獨佔.另外,我認為還有兩點需要特別考慮:
1. 如果有寫鎖請求在等待,則新的讀鎖請求也應該等待.否則程式可能永遠也沒有機會獲得寫鎖.
2. 一旦寫鎖被釋放,所有在等待中的讀鎖和寫鎖請求應該可以公平競爭,而不管請求的先後,只要之前已經在等待就應該有獲得鎖的機會.
如果說第一點還可以再斟酌一下的話,第二點應該是必須要保證的.

讀寫鎖的實現
總體思路比較簡單:讀寫鎖內部維護一些狀態值,用臨界端 CRITICAL_SECTION保護這些狀態值訪問的原子性和事件對象配合實現等待.

進入鎖的情況:
1. 如果當前鎖狀態為空白閑,則不管寫鎖還是讀鎖請求都允許進入,並設定相應的狀態值.

2. 如果當前鎖狀態是讀,新的寫鎖請求需要等待事件通知,並把寫鎖等待計數加一.
3. 如果當前鎖狀態是讀,新的讀鎖請求:
3.1 如果沒有寫鎖請求在等待,則允許讀鎖進入,並把讀鎖計數加一.
3.2 如果有寫鎖請求正在等待,則等待事件通知,並讀鎖等待計數加一(這樣做的目的如上文所述,要使寫鎖有機會進入).

4. 如果當前鎖狀態為寫,則不管讀鎖請求還是寫鎖請求,都等待事件通知並分別給讀鎖等待計數或者寫鎖等待計數加一.

解鎖的情況:
如果鎖釋放時,讀鎖等待計數或者寫鎖等待計數不為0,則觸發事件對象.

我使用手動事件對象,這樣的話一旦鎖被釋放,所有正在等待的鎖請求都將被啟用,然後重新以競爭臨界段的方式競爭鎖進入權以保證公平性.不管等待請求時的先後,只要是鎖釋放前進入等待狀態則鎖一旦釋放獲得進入權的機會是均等的.

後記
我在實現讀寫鎖之前,用Google搜尋過,找到的幾種Windows讀寫鎖實現都不甚理想,主要問題如下:
1. 等待事件對象時佔有了互斥量,這是一個非常不好的設計,這樣的話其他鎖請求將被阻塞在臨界段外部,只有一個鎖請求阻塞在Wait函數,對其他鎖請求是不公平的.
2. 沒考慮我開始提的目的2,即寫鎖可能永遠都沒機會進入.我不知道這個算不算缺點,但是自少在我的應用程式中,這種情況是不允許出現的,所以我重新設計過一個讀寫鎖.

實現過程中一個讓我很糾結的問題是:到底用自動事件還是手動事件. 用手動事件的好處是一旦觸發,所有等待中的請求都被啟用然後重新競爭,邏輯簡單明了.缺點是可能會有很多冗餘操作,比如有若干寫鎖還若干讀鎖請求正在等待進入,一旦鎖釋放,雖然全部請求(線程)都被啟用,但是肯定只有一個請求能夠進入,競爭失敗的請求測試一下條件後繼續掛起.如果使用自動事件,只有一個鎖請求線程會被喚醒(Wait函數的特點,被喚醒的那個線程等同於已經競爭成功),似乎效率更高一些.獲得進入權的鎖請求再根據等待的情況決定是否繼續觸發事件對象:如果還有讀請求和寫請求在等待,則不觸發;如果只有讀請求在等待,則再觸發一次以使其他讀請求可以進入.考慮再三,我還是決定採用手動事件,畢竟在讀鎖的數量遠大於寫鎖數量的情況下(這也是讀寫鎖比較常見的情境)速度更快一些(不需要等待多次事件).

附錄1 - C++代碼

#define RWLOCK_IDLE 0 /* 空閑 */#define RWLOCK_R 0x01 /* 讀鎖 */#define RWLOCK_W 0x02 /* 寫鎖 */class RWLock{private:int _st; /* 鎖狀態值 */int _rlockCount; /* 讀鎖計數 */int _rwaitingCount; /* 讀等待計數 */int _wwaitingCount; /* 寫等待計數 */HANDLE _ev; /* 通知事件 Event *///HANDLE _stLock; /* 訪問狀態值互斥量 */ /* 如果需要等待逾時,則用 Mutex */CRITICAL_SECTION _stLock;public:RWLock(void);~RWLock(void);void rlock();void wlock();void unlock();};RWLock::RWLock(void): _rlockCount(0),_st(RWLOCK_IDLE),_rwaitingCount(0),_wwaitingCount(0){//_stLock = CreateMutex(NULL, FALSE, NULL);//assert(_stLock != INVALID_HANDLE_VALUE);InitializeCriticalSection(&_stLock);/** 假設當前有多個讀鎖請求正在等待寫鎖釋放,那麼當寫鎖被釋放時,所有這些讀鎖都應該有機會獲得執行.*/_ev = CreateEvent(NULL, TRUE, FALSE, NULL);assert(_ev != INVALID_HANDLE_VALUE);}RWLock::~RWLock(void){//CloseHandle(_stLock);DeleteCriticalSection(&_stLock);CloseHandle(_ev);}void RWLock::rlock(){bool isWaitReturn = false;while(1){//WaitForSingleObject(_stLock, INFINITE);EnterCriticalSection(&_stLock);if(isWaitReturn){/** 等待事件返回,重新競爭鎖.*/--_rwaitingCount;}if(_st == RWLOCK_IDLE){/** 空閑狀態,直接得到控制權*/_st = RWLOCK_R;_rlockCount++;//ReleaseMutex(_stLock);LeaveCriticalSection(&_stLock);break;}else if( _st == RWLOCK_R){if(_wwaitingCount > 0){/** 有寫鎖正在等待,則一起等待,以使寫鎖能獲得競爭機會.*/++_rwaitingCount;ResetEvent(_ev);//SignalObjectAndWait(_stLock, _ev, INFINITE, FALSE);LeaveCriticalSection(&_stLock);/** 雖然 LeaveCriticalSection() 和 WaitForSingleObject() 之間有一個時間視窗,* 但是由於windows平台的事件訊號是不會丟失的,所以沒有問題.*/WaitForSingleObject(_ev, INFINITE);/** 等待返回,繼續嘗試加鎖.*/isWaitReturn = true;}else{/** 得到讀鎖,計數+1*/++_rlockCount;//ReleaseMutex(_stLock);LeaveCriticalSection(&_stLock);break;}}else if(_st == RWLOCK_W){/** 等待寫鎖釋放*/++_rwaitingCount;ResetEvent(_ev);//SignalObjectAndWait(_stLock, _ev, INFINITE, FALSE);LeaveCriticalSection(&_stLock);WaitForSingleObject(_ev, INFINITE);/** 等待返回,繼續嘗試加鎖.*/isWaitReturn = true;}else{assert(0);break;}}}void RWLock::wlock(){bool isWaitReturn = false;while(1){//WaitForSingleObject(_stLock, INFINITE);EnterCriticalSection(&_stLock);if(isWaitReturn) --_wwaitingCount;if(_st == RWLOCK_IDLE){_st = RWLOCK_W;//ReleaseMutex(_stLock);LeaveCriticalSection(&_stLock);break;}else{++_wwaitingCount;ResetEvent(_ev);//SignalObjectAndWait(_stLock, _ev, INFINITE, FALSE);LeaveCriticalSection(&_stLock);WaitForSingleObject(_ev, INFINITE);isWaitReturn = true;}}}void RWLock::unlock(){//WaitForSingleObject(_stLock, INFINITE);EnterCriticalSection(&_stLock);if(_rlockCount > 0){/* 讀鎖解鎖 */--_rlockCount;if( 0 == _rlockCount){_st = RWLOCK_IDLE;/* 釋放 */if( _wwaitingCount > 0 || _rwaitingCount > 0 ){/* * 此時有鎖請求正在等待,啟用所有等待的線程.(手動事件).* 使這些請求重新競爭鎖.*/SetEvent(_ev);}else{/* 空閑 */}}else{/* 還有讀鎖 */}}else{_st = RWLOCK_IDLE;/* 寫鎖解鎖 */if( _wwaitingCount > 0 || _rwaitingCount > 0 ){/* * 如果在佔有互斥量_stLock的情況下,觸發事件,那麼可能會使一些鎖請求不能得到競爭機會.* 假設調用unlock時,另一個線程正好調用rlock或者wlock.如果不釋放互斥量,只有之前已經等待的鎖請求有機會獲得鎖控制權.*/SetEvent(_ev);}else{/* 空閑 */}}//ReleaseMutex(_stLock);LeaveCriticalSection(&_stLock);}
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.