BOOL ConsumeElement(int nThreadNum, int nRequestNum, HWND hWndLB) { // Get access to the queue to consume a new element AcquireSRWLockShared(&g_srwLock); // Fall asleep until there is something to read. // Check if, while it was asleep, // it was not decided that the thread should stop while (g_q.IsEmpty(nThreadNum) && !g_fShutdown) { // There was not a readable element AddText(hWndLB, TEXT("[%d] Nothing to process"), nThreadNum); // The queue is empty // --> Wait until a writer adds a new element to read // and come back with the lock acquired in shared mode SleepConditionVariableSRW(&g_cvReadyToConsume, &g_srwLock, INFINITE, CONDITION_VARIABLE_LOCKMODE_SHARED); } // When thread is exiting, the lock should be released for writer // and readers should be signaled through the condition variable if (g_fShutdown) { // Show that the current thread is exiting AddText(hWndLB, TEXT("[%d] bye bye"), nThreadNum); // Another writer thread might still be blocked on the lock // --> release it before exiting ReleaseSRWLockShared(&g_srwLock); // Notify other readers that it is time to exit // --> release readers WakeConditionVariable(&g_cvReadyToConsume); return(FALSE); } // Get the first new element CQueue::ELEMENT e; // Note: No need to test the return value since IsEmpty // returned FALSE g_q.GetNewElement(nThreadNum, e); // No need to keep the lock any longer ReleaseSRWLockShared(&g_srwLock); // Show result of consuming the element AddText(hWndLB, TEXT("[%d] Processing %d:%d"), nThreadNum, e.m_nThreadNum, e.m_nRequestNum); // A free slot is now available for writer threads to produce // --> wake up a writer thread WakeConditionVariable(&g_cvReadyToProduce); return(TRUE); }