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

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

Jan 11, 2017
Coding
C++, CAS, Log

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