使用Windows.h互斥量实现生产者消费者线程的异常问题排查
生产者-消费者线程互斥量协作问题解决
问题原因分析
你的代码存在两个核心错误,导致线程无法正常协作:
- 主线程错误占用互斥量:主线程调用
WaitForSingleObject(hMutexReady, INFINITE)后,成为hMutexReady的拥有者,但后续生产者线程尝试ReleaseMutex(hMutexReady)时,由于生产者并非该互斥量的拥有者,释放操作无效,导致hMutexReady始终被主线程持有,消费者线程永远无法获取到它,陷入持续等待。 - 互斥量初始状态逻辑错误:两个互斥量的初始拥有状态设置不符合生产者-消费者的同步逻辑,导致生产者线程可以无限循环获取
hMutexHandled,却无法有效唤醒消费者。
修正后的代码
#include <windows.h> #include <stdio.h> #include <iostream> // 全局互斥量句柄 HANDLE hMutexReady; // 标记"信息已生成" HANDLE hMutexHandled; // 标记"信息已处理" // 全局数据缓冲区(示例) int g_data = 0; bool g_exit = false; // 生产者线程函数 DWORD WINAPI producer(LPVOID param) { while (!g_exit) { // 等待"信息已处理"互斥量,确保可以生产新数据 WaitForSingleObject(hMutexHandled, INFINITE); // 模拟从用户获取数据 std::cout << "请输入数据(输入-1退出):"; std::cin >> g_data; if (g_data == -1) { g_exit = true; } // 释放"信息已生成"互斥量,通知消费者可以处理 ReleaseMutex(hMutexReady); } return 0; } // 消费者线程函数 DWORD WINAPI consumer(LPVOID param) { while (!g_exit) { // 等待"信息已生成"互斥量,获取待处理数据 WaitForSingleObject(hMutexReady, INFINITE); if (g_exit) { break; } // 模拟处理数据 std::cout << "处理后的数据:" << g_data * 2 << std::endl; // 释放"信息已处理"互斥量,通知生产者可以继续生产 ReleaseMutex(hMutexHandled); } // 退出前释放互斥量,避免生产者阻塞 ReleaseMutex(hMutexHandled); return 0; } int _tmain(int argc, _TCHAR* argv[]) { // 创建互斥量: // hMutexReady初始由主线程拥有(消费者初始等待) // hMutexHandled初始未被拥有(生产者可立即开始) hMutexReady = CreateMutex(NULL, true, NULL); hMutexHandled = CreateMutex(NULL, false, NULL); // 创建生产者和消费者线程 HANDLE hProducer = CreateThread(NULL, 0, producer, NULL, 0, NULL); HANDLE hConsumer = CreateThread(NULL, 0, consumer, NULL, 0, NULL); HANDLE hThread[2] = {hProducer, hConsumer}; // 等待线程结束 WaitForMultipleObjects(2, hThread, true, INFINITE); // 清理资源 CloseHandle(hProducer); CloseHandle(hConsumer); CloseHandle(hMutexReady); CloseHandle(hMutexHandled); return 0; }
关键修正点说明
- 调整互斥量初始状态:
hMutexReady创建时设置bInitialOwner = true,让主线程初始拥有该互斥量,确保消费者线程一开始处于等待状态,直到生产者生产数据后释放它。hMutexHandled保持bInitialOwner = false,让生产者线程可以立即获取,开始第一次生产。
- 移除主线程对hMutexReady的错误占用:删除了主线程中
WaitForSingleObject(hMutexReady, INFINITE)的调用,避免主线程长期持有该互斥量。 - 添加退出逻辑:增加
g_exit标记,让用户可以通过输入-1终止程序,避免线程无限循环无法退出。 - 确保互斥量正确释放:每个线程仅在成功获取互斥量后才调用
ReleaseMutex,保证释放操作的有效性。
内容的提问来源于stack exchange,提问作者Timur Khalikshin
相关产品推荐
相关产品推荐

