生活随笔
收集整理的這篇文章主要介紹了
秒杀多线程第十一篇 读者写者问题
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
與上一篇《秒殺多線程第十篇 生產者消費者問題》的生產者消費者問題一樣,讀者寫者也是一個非常著名的同步問題。讀者寫者問題描述非常簡單,有一個寫者很多讀者,多個讀者可以同時讀文件,但寫者在寫文件時不允許有讀者在讀文件,同樣有讀者在讀文件時寫者也不去能寫文件。
上面是讀者寫者問題示意圖,類似于生產者消費者問題的分析過程,首先來找找哪些是屬于“等待”情況。
第一.寫者要等到沒有讀者時才能去寫文件。
第二.所有讀者要等待寫者完成寫文件后才能去讀文件。
找完“等待”情況后,再看看有沒有要互斥訪問的資源。由于只有一個寫者而讀者們是可以共享的讀文件,所以按題目要求并沒有需要互斥訪問的資源。類似于上一篇中美觀的彩色輸出,我們對生產者輸出代碼進行了顏色設置(在控制臺輸出顏色設置參見《VC 控制臺顏色設置》)。因此在這里要加個互斥訪問,不然很有可能在寫者線程將控制臺顏色設置還原之前,讀者線程就已經有輸出了。所以要對輸出語句作個互斥訪問處理,修改后的讀者及寫者的輸出函數如下所示:
[cpp]?view plaincopy
?? void?ReaderPrintf(char?*pszFormat,?...)?? {?? ????va_list???pArgList;?? ????va_start(pArgList,?pszFormat);?? ????EnterCriticalSection(&g_cs);?? ????vfprintf(stdout,?pszFormat,?pArgList);?? ????LeaveCriticalSection(&g_cs);?? ????va_end(pArgList);?? }?? ?? void?WriterPrintf(char?*pszStr)?? {?? ????EnterCriticalSection(&g_cs);?? ????SetConsoleColor(FOREGROUND_GREEN);?? ????printf("?????%s\n",?pszStr);?? ????SetConsoleColor(FOREGROUND_RED?|?FOREGROUND_GREEN?|?FOREGROUND_BLUE);?? ????LeaveCriticalSection(&g_cs);?? }??
讀者線程輸出函數所使用的可變參數詳見《C,C++中使用可變參數》。
???????解決了互斥輸出問題,接下來再考慮如何實現同步問題。可以設置一個變量來記錄正在讀文件的讀者個數,第一個開始讀文件的讀者要負責將關閉允許寫者進入的標志,最后一個結束讀文件的讀者要負責打開允許寫者進入的標志。這樣第一種“等待”情況就解決了。第二種“等待”情況是有寫者進入時所以讀者不能進入,使用一個事件就可以完成這個任務了——所有讀者都要等待這個事件而寫者負責觸發事件和設置事件為未觸發。詳細見代碼中注釋:
[cpp]?view plaincopy
?? #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,?g_cs_writer_count;?? HANDLE?g_hEventWriter,?g_hEventNoReader;?? int?g_nReaderCount;?? ?? 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());?? ?????? ????WaitForSingleObject(g_hEventWriter,?INFINITE);?? ?? ?????? ????EnterCriticalSection(&g_cs_writer_count);?? ????g_nReaderCount++;?? ????if?(g_nReaderCount?==?1)?? ????????ResetEvent(g_hEventNoReader);?? ????LeaveCriticalSection(&g_cs_writer_count);?? ?? ?????? ????ReaderPrintf("編號為%d的讀者開始讀取文件...\n",?GetCurrentThreadId());?? ?? ????Sleep(rand()?%?100);?? ?? ?????? ????ReaderPrintf("?編號為%d的讀者結束讀取文件\n",?GetCurrentThreadId());?? ?? ?????? ????EnterCriticalSection(&g_cs_writer_count);?? ????g_nReaderCount--;?? ????if?(g_nReaderCount?==?0)?? ????????SetEvent(g_hEventNoReader);?? ????LeaveCriticalSection(&g_cs_writer_count);?? ?? ????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("寫者線程進入等待中...");?? ?????? ????WaitForSingleObject(g_hEventNoReader,?INFINITE);?? ?????? ????ResetEvent(g_hEventWriter);?? ?????????? ?????? ????WriterPrintf("??寫者開始寫文件.....");?? ????Sleep(rand()?%?100);?? ????WriterPrintf("??寫者結束寫文件");?? ?? ?????? ????SetEvent(g_hEventWriter);?? ????return?0;?? }?? int?main()?? {?? ????printf("??讀者寫者問題\n");?? ????printf("?--?by?MoreWindows(?http://blog.csdn.net/MoreWindows?)?--\n\n");?? ?? ?????? ????InitializeCriticalSection(&g_cs);?? ????InitializeCriticalSection(&g_cs_writer_count);?? ?? ?????? ????g_hEventWriter?=?CreateEvent(NULL,?TRUE,?TRUE,?NULL);?? ????g_hEventNoReader??=?CreateEvent(NULL,?FALSE,?TRUE,?NULL);?? ????g_nReaderCount?=?0;?? ?? ????int?i;?? ????HANDLE?hThread[READER_NUM?+?1];?? ?????? ????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]);?? ?? ?????? ????CloseHandle(g_hEventWriter);?? ????CloseHandle(g_hEventNoReader);?? ????DeleteCriticalSection(&g_cs);?? ????DeleteCriticalSection(&g_cs_writer_count);?? ????return?0;?? }??
運行結果如下所示:
根據結果可以看出當有讀者在讀文件時,寫者線程會進入等待狀態中。當寫者線程在寫文件時,讀者線程也會排隊等待,說明讀者和寫者已經完成了同步。
?
本系列通過經典線程同步問題來列舉線程同步手段的關鍵段、事件、互斥量、信號量,并作對這四種方法進行了總結。然后又通過二個著名的線程同步實例——生產者消費者問題和讀者寫者問題來強化對多線程同步互斥的理解與運用。希望讀者們能夠熟練掌握,從而在筆試面試中能夠順利的“秒殺”多線程的相關試題,獲得自己滿意的offer。
?
從《秒殺多線程第十篇生產者消費者問題》到《秒殺多線程第十一篇讀者寫者問題》可以得出多線程問題的關鍵在于找到所有“等待”情況和判斷有無需要互斥訪問的資源。那么如何從實際問題中更好更快更全面的找出這些了?請看《秒殺多線程第十二篇多線程同步內功心法——PV操作上》和《秒殺多線程第十三篇多線程同步內功心法——PV操作下》這二篇以加強解決多線程同步問題的“內功”。??
?
另外,讀者寫者問題可以用讀寫鎖SRWLock來解決,請看《秒殺多線程第十四篇 讀者寫者問題繼 讀寫鎖SRWLock》
總結
以上是生活随笔為你收集整理的秒杀多线程第十一篇 读者写者问题的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。