[cpp] view plaincopy
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. //读者线程输出函数 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(\, pszStr); SetConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); LeaveCriticalSection(&g_cs); } 读者线程输出函数所使用的可变参数详见《C,C++中使用可变参数》。
解决了互斥输出问题,接下来再考虑如何实现同步问题。可以设置一个变量来记录正在读文件的读者个数,第一个开始读文件的读者要负责将关闭允许写者进入的标志,最后一个结束读文件的读者要负责打开允许写者进入的标志。这样第一种“等待”情况就解决了。第二种“等待”情况是有写者进入时所以读者不能进入,使用一个事件就可以完成这个任务了——所有读者都要等待这个事件而写者负责触发事件和设置事件为未触发。详细见代码中注释:
[cpp] view plaincopy
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. //读者与写者问题 #include #include #include //设置控制台输出颜色 BOOL SetConsoleColor(WORD wAttributes) { HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); if (hConsole == INVALID_HANDLE_VALUE) return FALSE; return SetConsoleTextAttribute(hConsole, wAttributes); } 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 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); 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98. 99. return 0; } //写者线程输出函数 void WriterPrintf(char *pszStr) { EnterCriticalSection(&g_cs); SetConsoleColor(FOREGROUND_GREEN); printf(\, 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(\); //初始化事件和信号量 InitializeCriticalSection(&g_cs); InitializeCriticalSection(&g_cs_writer_count); //手动置位,初始已触发 g_hEventWriter = CreateEvent(NULL, TRUE, TRUE, NULL); g_hEventNoReader = CreateEvent(NULL, FALSE, TRUE, NULL); 100. g_nReaderCount = 0; 101. 102. int i; 103. HANDLE hThread[READER_NUM + 1]; 104. //先启动二个读者线程 105. for (i = 1; i <= 2; i++) 106. hThread[i] = (HANDLE)_beginthreadex(NULL, 0, ReaderThreadFun, NULL, 0, NULL); 107. //启动写者线程 108. hThread[0] = (HANDLE)_beginthreadex(NULL, 0, WriterThreadFun, NULL, 0, NULL); 109. Sleep(50); 110. //最后启动其它读者结程 111. for ( ; i <= READER_NUM; i++) 112. hThread[i] = (HANDLE)_beginthreadex(NULL, 0, ReaderThreadFun, NULL, 0, NULL); 113. WaitForMultipleObjects(READER_NUM + 1, hThread, TRUE, INFINITE); 114. for (i = 0; i < READER_NUM + 1; i++) 115. CloseHandle(hThread[i]); 116. 117. //销毁事件和信号量 118. CloseHandle(g_hEventWriter); 119. CloseHandle(g_hEventNoReader); 120. DeleteCriticalSection(&g_cs); 121. DeleteCriticalSection(&g_cs_writer_count); 122. return 0; 123. } 运行结果如下所示:
根据结果可以看出当有读者在读文件时,写者线程会进入等待状态中。当写者线程在写文件时,读者线程也会排队等待,说明读者和写者已经完成了同步。