讀寫鎖在對資源進行保護的同時,還能區分想要讀取資源值的線程(讀取者線程)和想要更新資源的線程(寫入者線程)。對於讀取者線程,讀寫鎖會容許他們併發的執行。當有寫入者線程在佔有資源時,讀寫鎖會讓其它寫入者線程和讀取者線程等待。所以用讀寫鎖來解決讀者寫者問題會使代碼很是清晰和簡潔。html
SRWLock程序員
從visual studio2005開始你可使用SRWLock,和CRITICAL_SECTION(關鍵段)差不過的功能,不一樣的是由程序員控制讀寫線程,若是是讀線程,能夠同時讀取,若是是寫線程,則其餘線程掛起,寫完後立刻就能夠讀取windows
首先,須要一個SRWLOCK結構,而後調用InitializeSRWLock(PSWRLOCK srwLock)函數初始化這個結構.api
SRWLOCK srw; InitializeSRWLock(&srw); 一旦初始化完成,就能夠對寫入線程調用AcquireSRWLockExclusive()函數和ReleaseSRWLockExclusive()函數 AcquireSRWLockExclusive(&srw); //...寫入數據,寫入東西的時候該線程獨佔,其餘任何線程不可進入 ReleaseSRWLockExclusive(&srw); 對於只讀線程能夠調用AcquireSRWLockShared()函數和ReleaseSRWLockShared()函數,以下 AcquireSRWLockShared(&srw); //..讀取數據,若是這時沒有寫入數據則多個讀取線程能夠進行 ReleaseSRWLockShared)&srw);
Slim讀/寫鎖併發
SRWLock的目的和關鍵段相同:對一個資源進行保護,不讓其它線程訪問它。可是,與關鍵段不一樣的是,SRWLock容許咱們區分哪些想要讀取資源的值的線程(讀取者線程)和想要更新資源的值的線程(寫入者線程)。讓全部的讀取者線程在同一時刻訪問共享資源應該是可行的,這是由於僅僅讀取資源的值並不存在破壞數據的風險。只有當寫入者線程想要對資源進行更新的時候才須要進行同步。在這種狀況下,寫入者線程想要對資源進行更新的時候才須要進行同步。在這種狀況下,寫入者線程應該獨佔對資源的訪問權:任何其它線程,不管是讀取者線程仍是寫入者線程,都不容許訪問資源。這就是SRWLock提供的所有功能。函數
首先,咱們須要分配一個SRWLOCK結構並用InitializeSRWLock函數對它進行初始化:性能
VOID InitializeSRWLock(PSRWLOCK SRWLock);ui
一旦SRWLock初始化完成以後,寫入者線程就能夠調用AcquireSRWLockExclusive,將SRWLOCK對象的地址做爲參數傳入,以嘗試得到對被保護資源的獨佔訪問權。.net
VOID AcquireSRWLockExclusive(PSRWLOCK SRWLock);線程
完成對資源的更新以後,應該調用ReleaseSRWLockExclusice,並將SRWLOCK對象的地址做爲參數傳入,這樣就解除了對資源的鎖定。
VOID ReleaseSRWLockExclusive(PSRWLOCK SRWLock);
對讀取者線程來講,一樣有兩個步驟,單調用的是下面兩個新的函數:
VOID AcquireSRWLockShared(PSRWLOCK SRWLock);
VOID ReleaseSRWLockShared(PSRWLOCK SRWLock);
不存在用來刪除或銷燬SRWLOCK的函數,系統會自動執行清理工做。
與關鍵段相比,SRWLock缺少下面兩個特性:
1)不存在TryEnter(Shared/Exclusive)SRWLock 之類的函數:若是鎖已經被佔用,那麼調用AcquireSRWLock(Shared/Exclusive) 會阻塞調用線程。
2)不能遞歸地調用SRWLOCK。也就是說,一個線程不能爲了屢次寫入資源而屢次鎖定資源,而後再屢次調用ReleaseSRWLock* 來釋放對資源的鎖定。
總結,若是但願在應用程序中獲得最佳性能,那麼首先應該嘗試不要共享數據,而後依次使用volatile讀取,volatile寫入,Interlocked API,SRWLock以及關鍵段。當且僅當全部這些都不能知足要求的時候,再使用內核對象。由於每次等待和釋放內核對象都須要在用戶模式和內核模式之間切換,這種切換的CPU開銷很是大。
具體參考代碼:
//讀者與寫者問題繼 讀寫鎖SRWLock #include <stdio.h> #include <process.h> #include <windows.h> //設置控制檯輸出顏色 BOOL SetConsoleColor(WORD wAttributes) { HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); if (hConsole == INVALID_HANDLE_VALUE) return FALSE; return SetConsoleTextAttribute(hConsole, wAttributes); } const int READER_NUM = 5; //讀者個數 //關鍵段和事件 CRITICAL_SECTION g_cs; SRWLOCK g_srwLock; //讀者線程輸出函數(變參函數的實現) void ReaderPrintf(char *pszFormat, ...) { va_list pArgList; va_start(pArgList, pszFormat); EnterCriticalSection(&g_cs); vfprintf(stdout, pszFormat, pArgList); LeaveCriticalSection(&g_cs); va_end(pArgList); } //讀者線程函數 unsigned int __stdcall ReaderThreadFun(PVOID pM) { ReaderPrintf(" 編號爲%d的讀者進入等待中...n", GetCurrentThreadId()); //讀者申請讀取文件 AcquireSRWLockShared(&g_srwLock); //讀取文件 ReaderPrintf("編號爲%d的讀者開始讀取文件...n", GetCurrentThreadId()); Sleep(rand() % 100); ReaderPrintf(" 編號爲%d的讀者結束讀取文件n", GetCurrentThreadId()); //讀者結束讀取文件 ReleaseSRWLockShared(&g_srwLock); return 0; } //寫者線程輸出函數 void WriterPrintf(char *pszStr) { EnterCriticalSection(&g_cs); SetConsoleColor(FOREGROUND_GREEN); printf(" %sn", pszStr); SetConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); LeaveCriticalSection(&g_cs); } //寫者線程函數 unsigned int __stdcall WriterThreadFun(PVOID pM) { WriterPrintf("寫者線程進入等待中..."); //寫者申請寫文件 AcquireSRWLockExclusive(&g_srwLock); //寫文件 WriterPrintf(" 寫者開始寫文件....."); Sleep(rand() % 100); WriterPrintf(" 寫者結束寫文件"); //標記寫者結束寫文件 ReleaseSRWLockExclusive(&g_srwLock); return 0; } int main() { printf(" 讀者寫者問題繼 讀寫鎖SRWLockn"); printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --nn"); //初始化讀寫鎖和關鍵段 InitializeCriticalSection(&g_cs); InitializeSRWLock(&g_srwLock); HANDLE hThread[READER_NUM + 1]; int i; //先啓動二個讀者線程 for (i = 1; i <= 2; i++) hThread[i] = (HANDLE)_beginthreadex(NULL, 0, ReaderThreadFun, NULL, 0, NULL); //啓動寫者線程 hThread[0] = (HANDLE)_beginthreadex(NULL, 0, WriterThreadFun, NULL, 0, NULL); Sleep(50); //最後啓動其它讀者結程 for ( ; i <= READER_NUM; i++) hThread[i] = (HANDLE)_beginthreadex(NULL, 0, ReaderThreadFun, NULL, 0, NULL); WaitForMultipleObjects(READER_NUM + 1, hThread, TRUE, INFINITE); for (i = 0; i < READER_NUM + 1; i++) CloseHandle(hThread[i]); //銷燬關鍵段 DeleteCriticalSection(&g_cs); return 0; }