Win32多线程编程中写线程调用WaitForSingleObject阻塞问题求解
问题根源
你的实现是读优先的读写锁逻辑,核心问题是写线程饥饿,具体触发逻辑如下:
- 只要有至少1个读线程持有
hMutexRW,后续新到达的读线程不需要申请hMutexRW,直接可以进入读操作 - 若读线程持续不断发起读请求,
currentSemaphoreR会始终大于0,hMutexRW永远不会被释放,阻塞在WaitForSingleObject(hMutexRW, INFINITE)的写线程永远拿不到锁,陷入永久阻塞
除此之外你的代码还有两个隐含bug:
- 写线程中
waitTime、lastTime变量未初始化就直接使用,会输出随机值 - 读线程打开文件失败时没有提前处理读计数,可能导致锁泄漏
解决方法
修改为写优先逻辑,增加写等待计数,当有写线程等待时,禁止新的读线程进入读区,避免写线程被无限饿死。修改后的完整代码如下:
const int nThreadWriting = 5; const int nThreadReading = 5; const int nBufSize = 100; const int maxWritten = 10; const int timeRange = 3; HANDLE hMutexRW, hMutexR, hMutexN; int currentSemaphoreR = 0; int numberWritten = 0; // 新增:等待中的写线程计数 int waitingWriters = 0; unsigned __stdcall ThreadReading(void *pV) { HANDLE hFile; int waitTime, lastTime; char buf[nBufSize]; int* ptr = (int*)pV; srand(*ptr); while (1) { WaitForSingleObject(hMutexN, INFINITE); if (numberWritten >= maxWritten) { ReleaseMutex(hMutexN); return 0; } ReleaseMutex(hMutexN); waitTime = rand() % timeRange+1; lastTime = rand() % timeRange+1; // 先检查有没有写线程在等,有就先让写线程 WaitForSingleObject(hMutexR, INFINITE); while (waitingWriters > 0) { ReleaseMutex(hMutexR); Sleep(1); // 让出时间片给写线程 WaitForSingleObject(hMutexR, INFINITE); } if (currentSemaphoreR == 0) { WaitForSingleObject(hMutexRW, INFINITE); sprintf_s(buf, nBufSize, "%d R GET MUTEX_RW\n", GetCurrentThreadId()); std::cout << buf << std::endl; } currentSemaphoreR++; ReleaseMutex(hMutexR); hFile = CreateFile(TEXT("nice.txt"), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { std::cout << "cannot open file" << std::endl; // 读失败也要正确释放读计数 WaitForSingleObject(hMutexR, INFINITE); currentSemaphoreR--; if (currentSemaphoreR == 0) ReleaseMutex(hMutexRW); ReleaseMutex(hMutexR); continue; } sprintf_s(buf, nBufSize, "%d R %d %d\n", GetCurrentThreadId(), waitTime, lastTime); std::cout << buf <<std::endl; CloseHandle(hFile); WaitForSingleObject(hMutexR, INFINITE); currentSemaphoreR--; if (currentSemaphoreR == 0) ReleaseMutex(hMutexRW); ReleaseMutex(hMutexR); } return 0; } unsigned __stdcall ThreadWriting(void *pV) { int waitTime, lastTime; char buf[nBufSize]; int* ptr = (int*)pV; srand(*ptr); HANDLE hFile; while (1) { WaitForSingleObject(hMutexN, INFINITE); if (numberWritten >= maxWritten) { ReleaseMutex(hMutexN); return 0; } numberWritten++; ReleaseMutex(hMutexN); // 标记有写线程在等待 WaitForSingleObject(hMutexR, INFINITE); waitingWriters++; ReleaseMutex(hMutexR); WaitForSingleObject(hMutexRW, INFINITE); // 已经拿到锁,减少等待计数 WaitForSingleObject(hMutexR, INFINITE); waitingWriters--; ReleaseMutex(hMutexR); // 初始化变量,修复未初始化bug waitTime = rand() % timeRange+1; lastTime = rand() % timeRange+1; hFile = CreateFile(TEXT("nice.txt"), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { std::cout << "cannot open file" << std::endl; ReleaseMutex(hMutexRW); return 1; } SetFilePointer(hFile, 0, NULL, FILE_END); sprintf_s(buf, nBufSize, "%d W %d %d\n", GetCurrentThreadId(), waitTime, lastTime); WriteFile(hFile, buf, strlen(buf), NULL, NULL); CloseHandle(hFile); ReleaseMutex(hMutexRW); } return 0; } int main() { HANDLE hFile; hFile = CreateFile(TEXT("nice.txt"), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); CloseHandle(hFile); DWORD IDThread[nThreadWriting+ nThreadReading]; HANDLE hThread[nThreadWriting + nThreadReading]; int num[nThreadWriting + nThreadReading]; for (int i = 0; i < nThreadWriting + nThreadReading; i++) num[i] = i; hMutexRW = CreateMutex(NULL, FALSE, NULL); hMutexR = CreateMutex(NULL, FALSE, NULL); hMutexN = CreateMutex(NULL, FALSE, NULL); if (!hMutexRW || !hMutexR || !hMutexN) return 1; for (int i = 0; i < nThreadReading; i++) { hThread[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadReading, num + i, 0, (unsigned int*)&IDThread[i]); if (!hThread[i]) ExitProcess(3); } for (int i = nThreadReading; i < nThreadWriting + nThreadReading; i++) { hThread[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadWriting, num + i, 0, (unsigned int*)&IDThread[i]); if (!hThread[i]) ExitProcess(3); } WaitForMultipleObjects(nThreadWriting + nThreadReading, hThread, TRUE, INFINITE); for (int i = 0; i < nThreadWriting + nThreadReading; i++) CloseHandle(hThread[i]); CloseHandle(hMutexRW); CloseHandle(hMutexR); CloseHandle(hMutexN); return 0; }
额外优化建议
如果不需要手动实现读写锁,Win32原生提供了SRWLock读写锁API,使用更简单,性能也更高,不需要自行维护计数和互斥量逻辑。
内容的提问来源于stack exchange,提问作者strwake
相关产品推荐
相关产品推荐

