自旋锁 SpinLock

工作中某些需要,正好在codeproject 找到了,有兴趣看看~~

 

SpinLock.h

 

// (C) Copyright 2001 // Craig Henderson // // [email protected] // http://homepage.virgin.net/cdm.henderson // // SpinLock.h #ifndef __SPINLOCK_H_ #define __SPINLOCK_H_ // if we haven't got a DBG_FN macro defined for debug output, then // define it to be a simple TRACE #ifndef DBG_FN # define DBG_FN TRACE #endif // if we haven't got a TRACE macro defined for debug output, then // define a dummy to compile to a null statement #ifndef TRACE # define TRACE 1? (void)0 : (void)0 #endif // TRACE // if we haven't got an ASSERT macro defined for debugging, then // define a dummy to compile to a null statement #ifndef ASSERT # define ASSERT 1? (void)0 : (void)0 #endif // ASSERT // scope the class inside a namespace // this is always good practice to avoid clashes // with classes from external sources namespace cdmh { // helper function to determine if the runtime operating system is a // member of the WindowNT family // Returns: // true for WinNT 4, Window 2000 and WinXP // false for Windows 95, 98, 98SE and Me inline bool IsWindowsNT(void) { static OSVERSIONINFO osvi = { sizeof(OSVERSIONINFO) }; static bool got_version = false; if (!got_version) got_version = (::GetVersionEx(&osvi) == TRUE); return (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT); } // we use a trick with a numeric template parameter to enable multiple locks // and to avoid the need for a .cpp implementation file just to instantiate // the static m_lock member // each object that is instantiated with the same template parameter value // will share the lock, and different values of the template parameter will // identify independent locks // For example, an if an application has two classes of worker threads, each // class of thread requires exclusive access to a shared resource, but these // resources are independent of each other, then one class of thread can use // CSpinLock<1> and the other can use CSpinLock<2>. template<size_t N=1> class CSpinLock { private: // this is the value that will spin around the threads. the thread // with this value in it's m_plock member is the one that holds the // lock // this simplifies debugging as it is easy to see if a lock is held // by a lock object by casting the m_plock member to a char * in the // watch window enum { LockValue = 'kcol' }; // we declare a static member which will provide us with a cross-thread // value storing the actual value of the lock. each object that is // instantiated with the same template value will share the lock, and // different values of the template parameter will identify independent // locks static long m_lock; // this is the actual lock pointer that is used in the class. // it can be set through a parameter to the ctor, or by default, // uses the above member mutable long *m_plock; // SwitchToThread() is only available on the WindowsNT family, so we bind // dynamically at run-time so that we can still support Win9x family typedef BOOL (* PFNSwitchToThread)(void); PFNSwitchToThread m_pfnSwitchToThread; protected: // if these methods are not defined, then the compiler // will generate them if we try to use them. // we declare them but do not implement them as they are unused, // but if they are called in error, then we will get a linker // error. if we did not do this, the compiler will generate a basic // memcpy version, but that will be incorrect as the m_data vector // will not get copied correctly CSpinLock(const CSpinLock &); CSpinLock &operator=(const CSpinLock &); public: // this is the only public ctor // the parameter is optional, and can be omitted in most circumstances // to provide the default functionality. if the outer algorithm wants to // control the lock object more closely, then this parameter can be used explicit CSpinLock(long *plock = 0) { // if we haven't been supplied a lock variable, then use our own if (plock == NULL) m_plock = &m_lock; else m_plock = plock; // SwitchToThread() is only available on the WindowsNT family, so // we bind dynamically at run-time so that we can still support // Win9x family m_pfnSwitchToThread = NULL; if (cdmh::IsWindowsNT()) { HINSTANCE hInst = ::LoadLibrary("Kernel32.dll"); if (hInst != NULL) m_pfnSwitchToThread = (PFNSwitchToThread)::GetProcAddress(hInst, "SwitchToThread"); } } // class dtor ~CSpinLock() { // if this assert fails, then the object still holds the lock // when in is going out of scope ASSERT(!this->has_lock()); } void lock(void) const { // output to the debugger DBG_FN("0x%08x waiting for lock/n", this); // this is why it is called a spin lock; we simply sit in a loop // until the lock is released by the holding object. to make the // algorithm more efficient, we reliquish the processor to another // thread if we fail to get the lock // note that this is inefficient for anything more than the smallest // interval, as the execution thread does not block while the lock // is held while (::InterlockedExchange(m_plock, LockValue) != 0) { TRACE("0x%08x can't get lock/n", this); if (m_pfnSwitchToThread) m_pfnSwitchToThread(); } } // release the lock void unlock(void) const { // if this assert fails, then the object does not have the lock // while this is not an error in itself, and will not have any // adverse side effects, it generally signifies an error in the // outer algorithm ASSERT(this->has_lock()); // reset the lock value ::InterlockedExchange(m_plock, 0); // output to the debugger TRACE("0x%08x released lock/n", this); } // returns true/false to signify if the object currently holds the lock bool has_lock(void) const { return (*m_plock == LockValue); } }; // instantiate the static memory and initialise it // to zero - the unlocked state template<int N> long CSpinLock<N>::m_lock = 0; } // namespace cdmh #endif __SPINLOCK_H_

你可能感兴趣的:(Algorithm,thread,object,compiler,output,debugging)