工作中某些需要,正好在codeproject 找到了,有兴趣看看~~
// (C) Copyright 2001
// Craig Henderson
//
// cdm.henderson@virgin.net
// 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_