先举一个有bug的例子:
#include <iostream> #include <windows.h> #include <process.h> using namespace std; #define MAX_SIZE 0x500 HANDLE g_hSubmit; HANDLE g_hReturn; HANDLE g_hStop; char g_szInput[MAX_SIZE] = {0}; unsigned int _stdcall ThreadServer(void* pParam) { while (TRUE) { int nWaitRes = WaitForSingleObject(g_hStop, 100); if (WAIT_OBJECT_0 == nWaitRes) break; WaitForSingleObject(g_hSubmit, INFINITE); printf("Recieve:%s ", g_szInput); SetEvent(g_hReturn); } SetEvent(g_hStop); printf("Set stop "); return 0; } void main() { int count = 0; g_hSubmit = CreateEvent(NULL, FALSE, FALSE, NULL); g_hReturn = CreateEvent(NULL, FALSE, FALSE, NULL); g_hStop = CreateEvent(NULL, FALSE, FALSE, NULL); HANDLE hTheadServer = (HANDLE)_beginthreadex(NULL, 0, ThreadServer, NULL, 0, NULL); while (TRUE) { count++; printf("Input:"); cin.getline(g_szInput, MAX_SIZE); SetEvent(g_hSubmit); WaitForSingleObject(g_hReturn, INFINITE); if (count == 2){ Sleep(2000); SetEvent(g_hStop); break; } } HANDLE hHandle[3]; hHandle[0] = g_hStop; hHandle[1] = hTheadServer; WaitForMultipleObjects(2, hHandle, TRUE, INFINITE); CloseHandle(hTheadServer); CloseHandle(g_hReturn); CloseHandle(g_hStop); CloseHandle(g_hSubmit); getchar(); }
起初,我想要设置一个事件——g_hStop来通知线程,使得ThreadServer线程能够被主线程停止,但是这里出现了一个问题,如果我刻意让主线程Sleep2秒再去SetEvent,那么等待g_hStop的wait函数就会超时,从而往下继续执行等待Input,而此时主线程已经退出input循环,那么就会死锁。所以我改为使用全局变量来使得Threadserver线程退出:
#include <iostream> #include <windows.h> #include <process.h> using namespace std; #define MAX_SIZE 0x500 HANDLE g_hSubmit; HANDLE g_hReturn; HANDLE g_hStop; int g_nCount = 0; char g_szInput[MAX_SIZE] = {0}; unsigned int _stdcall ThreadServer(void* pParam) { while (TRUE) { if (g_nCount == 2) break; WaitForSingleObject(g_hSubmit, INFINITE); printf("Recieve:%s ", g_szInput); SetEvent(g_hReturn); } SetEvent(g_hStop); printf("Set stop "); return 0; } void main() { g_hSubmit = CreateEvent(NULL, FALSE, FALSE, NULL); g_hReturn = CreateEvent(NULL, FALSE, FALSE, NULL); g_hStop = CreateEvent(NULL, FALSE, FALSE, NULL); HANDLE hTheadServer = (HANDLE)_beginthreadex(NULL, 0, ThreadServer, NULL, 0, NULL); while (TRUE) { g_nCount++; printf("Input:"); cin.getline(g_szInput, MAX_SIZE); SetEvent(g_hSubmit); WaitForSingleObject(g_hReturn, INFINITE); if (g_nCount == 2){ Sleep(2000); SetEvent(g_hStop); break; } } HANDLE hHandle[2]; hHandle[0] = g_hStop; hHandle[1] = hTheadServer; WaitForMultipleObjects(2, hHandle, TRUE, INFINITE); CloseHandle(hTheadServer); CloseHandle(g_hReturn); CloseHandle(g_hSubmit); getchar(); }
三种方式改进:
1、如果你非要使用第一种情况,那么请把等待时间设置的长一些,不要是100毫秒,起码要是等待十秒,确保事件触发后,不会超时。
2、在代码设计的时候,就不要在while中使用两个waitforsingleobject,这种设计就给死锁带来了可能性:
3、使用waitformultiobject等待两个event之一,然后判断等到的是哪个,从而决定来做什么
一个Mutex和semaphore合用的例子:
#include <iostream> #include <windows.h> #include <vector> #include <process.h> using namespace std; #define MAX_SIZE 10 long g_ServerCount = 0; class CQueue{ public: CQueue(); ~CQueue(); void Append(); void Remove(); private: vector<int> m_vecQueue; HANDLE m_hMutex; HANDLE m_hSem; HANDLE m_hHandles[2]; }; CQueue g_c; CQueue::CQueue() { m_hMutex = CreateMutex(NULL, FALSE, NULL); m_hSem = CreateSemaphore(NULL, 0, 10, NULL); m_hHandles[0] = m_hMutex; m_hHandles[1] = m_hSem; } CQueue::~CQueue() { CloseHandle(m_hMutex); CloseHandle(m_hSem); } void CQueue::Append(){ DWORD dwRet = WaitForSingleObject(m_hMutex, INFINITE); InterlockedExchangeAdd(&g_ServerCount, 1); if (dwRet == WAIT_OBJECT_0) { LONG lPrevCount; int bRet = ReleaseSemaphore(m_hSem, 1, &lPrevCount); if (bRet) { m_vecQueue.push_back(g_ServerCount); printf("Add element %d ", g_ServerCount); } } ReleaseMutex(m_hMutex); } void CQueue::Remove() { DWORD dwRet = WaitForMultipleObjects(2, m_hHandles, TRUE, INFINITE); if (WAIT_OBJECT_0 == dwRet) { printf("Remove element %d ", m_vecQueue.back()); m_vecQueue.pop_back(); } ReleaseMutex(m_hMutex); } unsigned int _stdcall ServerThread(void* pParam) { while (TRUE) { Sleep(20); g_c.Append(); } return 0; } unsigned int _stdcall ClientThread(void* pParam) { while (TRUE) { Sleep(20); g_c.Remove(); } return 0; } void main() { HANDLE h_Handles[3]; h_Handles[0] = (HANDLE)_beginthreadex(NULL, 0, ServerThread, NULL, 0, NULL); h_Handles[1] = (HANDLE)_beginthreadex(NULL, 0, ServerThread, NULL, 0, NULL); h_Handles[2] = (HANDLE)_beginthreadex(NULL, 0, ClientThread, NULL, 0, NULL); WaitForMultipleObjects(_countof(h_Handles), h_Handles, TRUE, INFINITE); for (int i = 0; i < _countof(h_Handles); i++) CloseHandle(h_Handles[i]); getchar(); }