通常自旋锁分三种: 纯自旋,队列自旋锁和MCS 锁。 MCS 锁也是基于队列自旋锁的。 但最基本的还是纯自旋锁了。
自旋锁使用场景:
1. 通常在一些需要短暂等待的情况下被使用。通常用sleep(0) 也可以办到,但sleep无法控制不切换上下文。
2. 当多线程锁使用。通常系统底层用来同步多个CPU访问。
纯自旋锁实现如下:
class SpinLock
{
public:
bool IsLock()
{
return (m_iFlag ==1);
}
int Lock()
{
while(InterlockedCompareExchange(m_iFlag,1,0) != 0)
{
//不成功,表示其他线程已经加锁了
// 自旋等待
Sleep(0);
}
return 0;
}
void UnLock()
{
m_iFlag=0;
}
private:
volatile int m_iFlag;
};
通过在m_iFlag 变量上自旋等待来实现锁。
在lock 函数里 ,通过sleep(0) 来自旋,但对效率要求较高的时候, sleep(0) 通常不合适,改进如下:
int lock()
{
while(InterlockedCompareExchange(m_iFlag,1,0) != 0)
{
//不成功,表示其他线程已经加锁了
// 自旋等待
YieldProcessor();
}
return 0
}
不支持yieldProcessor的系统上,可以使用计数:
int lock()
{
while(InterlockedCompareExchange(m_iFlag,1,0) != 0)
{
int count = 1000;
while(--count);
}
return 0;
}
通常认为自旋容易产生活锁,因为如果1000个线程都 用count =1000 来等待的话,会在InterlockedCompareExchange函数里产生大量无用的竞争。改进如下:
int lock()
{
while(InterlockedCompareExchange(m_iFlag,1,0) != 0)
{
srand(GetTickCount());
int count = 500+rand()%500;
while(--count);
}
return 0;
}
自旋锁一般不使用在较长等待的情况下。 如果等待太长时间会怎么样呢? 这里肯定会太浪费CPU了。但我们长须处理长时间等待这种特殊情况:
int Lock()
{
int iWastCount = 10;
while(InterlockedCompareExchange(m_iFlag,1,0) != 0){
if( --iWastCount<=0)
{
Sleep(0);
}
else {
srand(GetTickCount());
int count = 500+rand()%500;
while(--count);
}
}
return 0;
}