CAS 的跨平台实现方案 以及 基于CAS 的无锁多线程安全日志类
Jan 11, 2017
CAS #
CAS, compare-and-swap, 原子的比较和设置变量值。
#include <stdio.h>
#if defined(__linux__) || defined(__unix__)
#define CAS32(ptr, val_old, val_new)({ char ret; __asm__ __volatile__("lock; cmpxchgl %2,%0; setz %1": "+m"(*ptr), "=q"(ret): "r"(val_new),"a"(val_old): "memory"); ret;})
//or using #define CAS __sync_bool_compare_and_swap, this requires -lstdc++
#elif _WIN32
#include <windows.h>
bool CAS32(int* ptr,int val_old, int val_new){
InterlockedCompareExchange((long *) ptr,val_new,val_old);
return (*ptr == val_new) ;
}
#else
#error "unknown os"
#endif
int main()
{
int v = 10, v_old = 10, v_new = 11;
if(CAS32(&v,v_old,v_new)){
printf("CAS32 ok, v=%d\n",v);
}else{
printf("CAS32 failed, v=%d\n",v);
}
return 0;
}
基于CAS 的无锁多线程安全日志类 #
日志类为单例类,通过对类的成员变量 occupied_ 的原子操作(CAS)来实现线程安全。
写日志时,通过while( !CAS(xx) ) 来检查并设置occupied_参数,如果检测到occupied_为0,即无其它线程使用,则设置为1,防止其它线程使用,日志写完后,再通过CAS将其设置成1,即未占用。
目前就是简单的将日志输出到控制台,如果将日志输出到日志文件,就会发现CAS同步的用处,它保证了日志文件不会被多个线程同时占用。
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#define MAX_LOG_BUF_SIZE 2048
#define CAS __sync_bool_compare_and_swap
class SimpleLockFreeLog
{
private:
SimpleLockFreeLog(void):occupied_(0),cnt_(0){};
~SimpleLockFreeLog(void){};
public:
// static SimpleLockFreeLog& GetInstance()
// {
// static SimpleLockFreeLog instance; //c++ 11
// return instance;
// }
static SimpleLockFreeLog& GetInstance(){
static SimpleLockFreeLog* pInstance = NULL;
if(NULL == pInstance) pInstance = new SimpleLockFreeLog();
return *pInstance;
}
void LogError(const char* fmt,...){
while (CAS(&occupied_, 0, 1) != true){}
va_list args;
va_start(args,fmt);
static char bufPrint[MAX_LOG_BUF_SIZE] = {0};
memset(bufPrint,0,MAX_LOG_BUF_SIZE);
int n = 0;
n = vsnprintf(bufPrint,MAX_LOG_BUF_SIZE-1,fmt,args);
if(n < 0 || n > MAX_LOG_BUF_SIZE) return;
va_end(args);
printf("cnt: %d log: %s",cnt_++, bufPrint);
//printf("%s",bufPrint);
CAS(&occupied_,1,0);
}
private:
unsigned int occupied_;
int cnt_;
};
#define SimpleLockFreeLog_Error(fmt,...) SimpleLockFreeLog::GetInstance().LogError("[%s][%s:%d][ERROR]:"fmt,__TIME__,__FILE__,__LINE__,##__VA_ARGS__);
使用的demo 如下
#include <pthread.h>
#include "SimpleLockFreeLog.h"
void* routine(void* arg)
{
const char* pTid = (const char*) arg;
for(int i = 1; i<= 30; i++){
SimpleLockFreeLog_Error("%s: %d\n",pTid,i);
}
return 0;
}
int main()
{
SimpleLockFreeLog_Error("Start...\n");
pthread_t tid1,tid2,tid3;
pthread_create(&tid1,NULL,routine,(void*)"tid1");
pthread_create(&tid2,NULL,routine,(void*)"tid2");
pthread_create(&tid3,NULL,routine,(void*)"tid3");
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
pthread_join(tid3,NULL);
SimpleLockFreeLog_Error("End...\n");
return 0;
}
编译方法
gcc main.cpp -o LogDemo -lstdc++ -lpthread
如果不使用CAS,会发现每个线程的打印会互相打乱,使用CAS时,每个线程的打印都是有序的。