昨天完成了无锁队列的实现,然后思考了一下锁的原理,锁的核心无非是保证有一个变量需要被原子性地更新,比如,我们用一个bool x,表示锁,当我们获取锁的时候,我们需要保证的是锁能够只在同一时间被多个线程中的一个线程去读取和更新,在逻辑上有如下的代码:
bool GetLock(bool& x){
if(x){
x= false;
return true;
}
return false;
}
后面写的是一段测试代码,有兴趣的同学可以看看。
bool GetLock(bool& x){
if(x){
x= false;
return true;
}
return false;
}
然而,这里面的读取和更新不是原子操作的,如果两个线程同时发现了x是true,但是,还没有一个线程更新了x,此时,就会有两个线程同时更新了x,也就是有两个线程同时更新了锁的状态,造成这种原因在于x的读取和更新不是原子性的。X86里面有个CMPXCHG 指令,Windows 里面有个InterLockCompareExchange API都能运行这种原子性的操作,也就是说,我们可以利用这两个原子操作来完成锁的实现。以下是代码
#include <windows.h>
#pragma comment(lib, "kernel32.lib")
class USLock{//user space lock
public:
USLock(){
LockData= new int(1);
}
~USLock(){
delete LockData;
}
public:
bool Get(){
return (InterlockedCompareExchange((LONG*)LockData,0,1));
}
void Release(){
*LockData= 1;
}
private:
int *LockData;
};
DWORD WINAPI CS(void* lpAram);
#include <queue>
#include <iostream>
using namespace std;
deque<int> q;
USLock lock;
int main(){
HANDLE hThread[2];
for(int i= 0; i< 2; ++i){
hThread[i]= ::CreateThread(NULL,0,CS,(void*)i,0,NULL);
}
::WaitForMultipleObjects(2,hThread,true,INFINITE);
}
DWORD WINAPI CS(void* lpAram){
while(true){
if(!lpAram){//生产
for(int i= 0; i< 1000; ){
if(lock.Get()){
q.push_front(i++);
lock.Release();
}
::Sleep(rand()%1000);
}
return 0;
}else{
if(lock.Get()){//消费
if(!q.empty()){
cout<<q.back();
q.pop_back();
}
lock.Release();
}
}
::Sleep(rand()%1000);
}
return 0;
}
后面写的是一段测试代码,有兴趣的同学可以看看。