在《秒殺多線程第十一篇讀者寫者問題》文章中我們使用事件和一個記錄讀者個數的變數來解決讀者寫者問題。問題雖然得到瞭解決,但代碼有點複雜。本篇將介紹一種新方法——讀寫鎖SRWLock來解決這一問題。讀寫鎖在對資源進行保護的同時,還能區分想要讀取資源值的線程(讀取者線程)和想要更新資源的線程(寫入者線程)。對於讀取者線程,讀寫鎖會允許他們並發的執行。當有寫入者線程在佔有資源時,讀寫鎖會讓其它寫入者線程和讀取者線程等待。因此用讀寫鎖來解決讀者寫者問題會使代碼非常清晰和簡潔。
下面就來看看如何使用讀寫鎖,要注意編譯讀寫鎖程式需要VS2008,運行讀寫鎖程式要在Vista或Windows Server2008系統(比這兩個更進階的系統也可以)。讀寫鎖的主要函數就五個,分為初始化函數,寫入者線程申請和釋放函數,讀取者線程申請和釋放函數,以下是詳細的函數使用說明:
第一個 InitializeSRWLock
函數功能:初始化讀寫鎖
函數原型:VOID InitializeSRWLock(PSRWLOCK SRWLock);
函數說明:初始化(沒有刪除或銷毀SRWLOCK的函數,系統會自動清理)
第二個 AcquireSRWLockExclusive
函數功能:寫入者線程申請寫資源。
函數原型:VOID AcquireSRWLockExclusive(PSRWLOCK SRWLock);
第三個 ReleaseSRWLockExclusive
函數功能:寫入者線程寫資源完畢,釋放對資源的佔用。
函數原型:VOID ReleaseSRWLockExclusive(PSRWLOCK SRWLock);
第四個 AcquireSRWLockShared
函數功能:讀取者線程申請讀資源。
函數原型:VOID AcquireSRWLockShared(PSRWLOCK SRWLock);
第五個 ReleaseSRWLockShared
函數功能:讀取者線程結束讀取資源,釋放對資源的佔用。
函數原型:VOID ReleaseSRWLockShared(PSRWLOCK SRWLock);
注意一個線程僅能鎖定資源一次,不能多次鎖定資源。
使用讀寫鎖精簡後的代碼如下(代碼中變參函數的實現請參閱《C,C++中使用可變參數》,控制台顏色設定請參閱《VC 控制台顏色設定》):
//讀者與寫者問題繼 讀寫鎖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(" %s\n", 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(" 讀者寫者問題繼 讀寫鎖SRWLock\n");printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");//初始化讀寫鎖和關鍵段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;}
對比下《秒殺多線程第十一篇讀者寫者問題》中的代碼就可以發現這份代碼確實清爽許多了。這個程式用VS2008編譯可以通過,但在XP系統下運行會導致報錯。
在Win7系統下能夠正確的運行,結果:
最後總結一下讀寫鎖SRWLock
1.讀寫鎖聲明後要初始化,但不用銷毀,系統會自動清理讀寫鎖。
2.讀取者和寫入者分別調用不同的申請函數和釋放函數。
轉載請標明出處,原文地址:http://blog.csdn.net/morewindows/article/details/7650574
如果覺得本文對您有協助,請點擊‘頂’支援一下,您的支援是我寫作最大的動力,謝謝。