// intrinExample.cpp // compile with: /EHsc /O2 // Simple example of using _Interlocked* intrinsics to // do manual synchronization // // Add [-DSKIP_LOCKING] to the command line to disable // the locking. This will cause the threads to execute out // of sequence. #include "windows.h" #include <iostream> #include <queue> #include <intrin.h> using namespace std; // -------------------------------------------------------------------- // if defined, will not do any locking on shared data //#define SKIP_LOCKING // A common way of locking using _InterlockedCompareExchange. // Please refer to other sources for a discussion of the many issues // involved. For example, this particular locking scheme performs well // when lock contention is low, as the while loop overhead is small and // locks are acquired very quickly, but degrades as many callers want // the lock and most threads are doing a lot of interlocked spinning. // There are also no guarantees that a caller will ever acquire the // lock. namespace MyInterlockedIntrinsicLock { typedef unsigned LOCK, *PLOCK; #pragma intrinsic(_InterlockedCompareExchange, _InterlockedExchange) enum {LOCK_IS_FREE = 0, LOCK_IS_TAKEN = 1}; void Lock(PLOCK pl) { #if !defined(SKIP_LOCKING) // If *pl == LOCK_IS_FREE, it is set to LOCK_IS_TAKEN // atomically, so only 1 caller gets the lock. // If *pl == LOCK_IS_TAKEN, // the result is LOCK_IS_TAKEN, and the while loop keeps spinning. while (_InterlockedCompareExchange((long *)pl, LOCK_IS_TAKEN, // exchange LOCK_IS_FREE) // comparand == LOCK_IS_TAKEN) { // spin! // call __yield() here on the IPF architecture to improve // performance. } // This will also work. //while (_InterlockedExchange(pl, LOCK_IS_TAKEN) == // LOCK_IS_TAKEN) //{ // // spin! //} // At this point, the lock is acquired. #endif } void Unlock(PLOCK pl) { #if !defined(SKIP_LOCKING) _InterlockedExchange((long *)pl, LOCK_IS_FREE); #endif } } // ------------------------------------------------------------------ // Data shared by threads queue<int> SharedQueue; MyInterlockedIntrinsicLock::LOCK SharedLock; int TicketNumber; // ------------------------------------------------------------------ DWORD WINAPI ProducerThread( LPVOID unused ) { while (1) { // Acquire shared data. Enter critical section. MyInterlockedIntrinsicLock::Lock(&SharedLock); //cout << ">" << TicketNumber << endl; SharedQueue.push(TicketNumber++); // Release shared data. Leave critical section. MyInterlockedIntrinsicLock::Unlock(&SharedLock); Sleep(rand() % 20); } return 0; } DWORD WINAPI ConsumerThread( LPVOID unused ) { while (1) { // Acquire shared data. Enter critical section MyInterlockedIntrinsicLock::Lock(&SharedLock); if (!SharedQueue.empty()) { int x = SharedQueue.front(); cout << "<" << x << endl; SharedQueue.pop(); } // Release shared data. Leave critical section MyInterlockedIntrinsicLock::Unlock(&SharedLock); Sleep(rand()%20); } return 0; } int main( void ) { const int timeoutTime = 500; int unused1, unused2; HANDLE threads[4]; // The program creates 4 threads: // two producer threads adding to the queue // and two consumers taking data out and printing it. threads[0] = CreateThread(NULL, 0, ProducerThread, &unused1, 0, (LPDWORD)&unused2); threads[1] = CreateThread(NULL, 0, ConsumerThread, &unused1, 0, (LPDWORD)&unused2); threads[2] = CreateThread(NULL, 0, ProducerThread, &unused1, 0, (LPDWORD)&unused2); threads[3] = CreateThread(NULL, 0, ConsumerThread, &unused1, 0, (LPDWORD)&unused2); WaitForMultipleObjects(4, threads, TRUE, timeoutTime); return 0; }
Output
<0 <1 <2 <3 <4 <5 <6 <7 <8 <9 <10 <11 <12 <13 <14 <15 <16 <17 <18 <19 <20 <21 <22 <23 <24 <25 <26 <27 <28 <29
END Microsoft Specific