Well, in the really world, there are so many things you have to wait for, my wages up, my promotion, my princess…and those things never send me a signal. Wait for single object seems to be disparate and hopeless, while wait for multiple objects seems better. While in my code, I am the GOD, I can do anything I want with my code. To some extent programming satisfied one’s desire for governing and controlling. Well I went too far away. Let me go back to the subject.
There are many ways to implement synchronization: event, mutex, critical section, semaphore.
Every object has it’s own advantage, such as event can be used for both sync and notification, mutex and event can be used across processes, critical section is easy to use, semaphore can manage more than one resource, and so on. So many details, using them seems to be a big trouble.
Here a synchronization class is introduced to handle those problems:
Here is the code:
#include <windows.h> //---------------------------- CLASS ---------------------------------------------------------- // Implementation of the critical section class QMutex { private: CRITICAL_SECTION m_Cs; public: QMutex() { ::InitializeCriticalSection(&this->m_Cs); } ~QMutex() { ::DeleteCriticalSection(&this->m_Cs); } void Lock() { ::EnterCriticalSection(&this->m_Cs); } BOOL TryLock() { return (BOOL) ::TryEnterCriticalSection(&this->m_Cs); } void Unlock() { ::LeaveCriticalSection(&this->m_Cs); } }; // Implementation of the event for notify class QEvent { private: HANDLE m_Cs; public: QEvent() { m_Cs = ::CreateEvent(NULL, FALSE, TRUE, "NotifyEvent"); } ~QEvent() { ::CloseHandle(m_Cs); } void wait() { ::WaitForSingleObject(this->m_Cs, INFINITE); } //BOOL TryLock() { return (BOOL) ::TryEnterCriticalSection(&this->m_Cs); } void notify() { ::SetEvent(this->m_Cs); } }; //---------------------------- CLASS ---------------------------------------------------------- // Implementation of the semaphore class QSemaphore { private: HANDLE m_hSemaphore; long m_lMaximumCount; public: QSemaphore(long lInitialCount, long lMaximumCount) { this->m_hSemaphore = ::CreateSemaphore(NULL, lInitialCount, lMaximumCount, NULL); if (this->m_hSemaphore == NULL) throw "Call to CreateSemaphore() failed. Could not create semaphore."; this->m_lMaximumCount = lMaximumCount; }; ~QSemaphore() { ::CloseHandle(this->m_hSemaphore); }; long GetMaximumCount() const { return this->m_lMaximumCount; }; void Inc() { ::WaitForSingleObject(this->m_hSemaphore, INFINITE); }; void Dec() { ::ReleaseSemaphore(this->m_hSemaphore, 1, NULL); }; void Dec(long lCount) { ::ReleaseSemaphore(this->m_hSemaphore, lCount, NULL); }; };
With the class, here is a example of Producer/Consumer model.
const int RESOURCE_NUM = 2; QSemaphore g_pFullQSemaphore(0, RESOURCE_NUM); QSemaphore g_pEmptyQSemaphore(RESOURCE_NUM, RESOURCE_NUM); QMutex g_BufStatusQMutex; // sync the buffer status const int ERROR_BUF_INDEX = -1; BOOL g_aBufStatus[RESOURCE_NUM] = {0}; // TRUE for full; FALSE for empty // get an empty buffer int getEmptyIndex() { return FALSE == g_aBufStatus[0] ? 0 : (FALSE == g_aBufStatus[1] ? 1 : -1); } // get a full buffer int getFullIndex() { return TRUE == g_aBufStatus[0] ? 0 : (TRUE == g_aBufStatus[1] ? 1 : -1); } void setBufState(int iBufIndex, BOOL bState) { g_BufStatusQMutex.Lock(); g_aBufStatus[iBufIndex] = bState; g_BufStatusQMutex.Unlock(); } DWORD WINAPI ProduceThread(LPVOID lpParameter) { // 1. record ReadRecordThreadInfo ReadRecordThreadInfo *pThInof = (ReadRecordThreadInfo *)lpParameter; apBuf[0] = pThInof->pBufA; apBuf[1] = pThInof->pBufB; PriorityST *pPriorityST = pThInof->pPriorityST; CDlg *p_this = (CDlg *)pThInof->pDialog; while (TRUE) { // get free buffer int iBufIndex = -1; // indicate the free buffer g_pEmptyQSemaphore.Inc(); if (ERROR_BUF_INDEX == (iBufIndex = getEmptyIndex())) continue; // produce data int iRecordCount = 0; iRecordCount = pClass->getAllRecord(apBuf[iBufIndex], iEmmCount, pPriorityST); if (iRecordCount != iEmmCount) break; // notify send thread setBufState(iBufIndex, TRUE); g_pFullQSemaphore.Dec(); Sleep(0); } return 0; } DWORD WINAPI consumeThread(LPVOID lpParameter) { // 1. record ReadRecordThreadInfo ReadRecordThreadInfo *pThInof = (ReadRecordThreadInfo *)lpParameter; apBuf[0] = pThInof->pBufA; apBuf[1] = pThInof->pBufB; PriorityST *pPriorityST = pThInof->pPriorityST; CDlg *p_this = (CDlg *)pThInof->pDialog; while (TRUE) { int iBufIndex = -1; g_pFullQSemaphore.Inc(); if (ERROR_BUF_INDEX == (iBufIndex = getFullIndex())) continue; // consume the data for (int j = 0; j < iEmmCount; ++j, ++iCountSended) { //.... } setBufState(iBufIndex, FALSE); g_pEmptyQSemaphore.Dec(); Sleep(0); } return 0; } // start thread void CEmmGeneratorDlg::OnBnClickedButtonBegin() { // TODO: Add your control notification handler code here static ReadRecordThreadInfo ThInof; ThInof.iEmmCount = 30; ThInof.pBufA = new EmmMessage[ThInof.iEmmCount]; ThInof.pBufB = new EmmMessage[ThInof.iEmmCount]; ThInof.pDialog = this; HANDLE hTheadR = CreateThread(NULL, 0, readEmmThread, (LPVOID)&ThInof, NULL, NULL); HANDLE hTheadW = CreateThread(NULL, 0, sendEmmThread, (LPVOID)&ThInof, NULL, NULL);