β

CAS 的跨平台实现方案 以及 基于CAS 的无锁多线程安全日志类

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时,每个线程的打印都是有序的。

乱弹

发表评论