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时,每个线程的打印都是有序的。
作者:JarvisChu
原文链接:CAS 的跨平台实现方案 以及 基于CAS 的无锁多线程安全日志类
版权声明:自由转载-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0