高并发服务器经常用到多线程编程,需要对共享数据进行操作,为了保护数据的正确性,有一种有效的方法就是加锁pthread_mutex_t。但是加锁会引起性能的下降,多个线程竞争同一个锁,抢占失败后强制上下文切换。还有一种方法就是使用原子指令。有一个重要的方法叫做CAS(compare and swap)。下面是CAS的伪代码
int compare_and_swap(int* reg, int oldval, int newval)
{
ATOMIC();
int old_reg_val = *reg;
if (old_reg_val == oldval)
*reg = newval;
END_ATOMIC();
return old_reg_val;
}
使用的时候一般都是定义三个全局变量(int reg=0,int old=0,int new=1;),所有线程都可以访问到。
int reg=0,int old=0,int new=1;
main()
{
do{
int ret = compare_and_swap(®,old,new);
}while(ret!=reg)
//只有ret!=reg的线程才可以通过
}
下面计算count的值,多个线程计算1加到400000。分别统计使用锁和原子操作使用的时间。
/*************************************************************************
> File Name: threadsync.c
> Author: chenhui
> Mail: *********
> Created Time: 2016年02月29日 21:53:40
************************************************************************/
#include
#include
#include
#include
#include
#include "threadLock.h"
int64_t count=0;
int64_t count2=0;
int atomic_mutex = 0;
int lock = 0;
int unlock = 1;
pthread_mutex_t count_mutex=PTHREAD_MUTEX_INITIALIZER;
void *addNum(void*)//.c文件时没加void*,可以编译运行。.cpp文件时编译不过(pthread_create(,,addNum,))提示类型转换无效
{
int i=1;
for(;i<=400000;i++)
{
ThreadLock lock(&count_mutex);
count+=i;
}
}
void *addNum2(void*)
{
int i=1;
for(;i<=400000;i++)
{
CASFreeLock casLock(atomic_mutex,lock,unlock);
count2+=i;
}
}
int64_t getSystemTimeMs()
{
struct timeval timeNow;
gettimeofday(&timeNow, NULL);
return timeNow.tv_sec*1000 + timeNow.tv_usec/1000;
}
int main()
{
int64_t timeStart,timeEnd;
timeStart = getSystemTimeMs();
pthread_t p1,p2,p3,p4;
pthread_create(&p1,NULL,addNum,NULL);
pthread_create(&p2,NULL,addNum,NULL);
pthread_create(&p3,NULL,addNum,NULL);
pthread_create(&p4,NULL,addNum,NULL);
pthread_join(p1,NULL);
pthread_join(p2,NULL);
pthread_join(p3,NULL);
pthread_join(p4,NULL);
timeEnd = getSystemTimeMs();
printf("thread lock->count:%lld, time use=%lldms\n",count, (timeEnd-timeStart));
timeStart = getSystemTimeMs();
pthread_create(&p1,NULL,addNum2,NULL);
pthread_create(&p2,NULL,addNum2,NULL);
pthread_create(&p3,NULL,addNum2,NULL);
pthread_create(&p4,NULL,addNum2,NULL);
pthread_join(p1,NULL);
pthread_join(p2,NULL);
pthread_join(p3,NULL);
pthread_join(p4,NULL);
timeEnd = getSystemTimeMs();
printf("cas lock free->count2:%lld, time use=%lldms\n",count2, (timeEnd-timeStart));
return 0;
}
threadLock.h文件
#ifndef THREAD_LOCK
#define THREAD_LOCK
#include
#include
#include
#include
class ThreadLock
{
private:
pthread_mutex_t* mutexPtr;
public:
explicit inline ThreadLock(pthread_mutex_t *pm):mutexPtr(pm)//explicit 抑制隐式转换,inline加入符号表提高函数调用效率
{
//printf("lock of threadid:%d\n",pthread_self());//放在这里,打印出来的信息不准
pthread_mutex_lock(mutexPtr);
//printf("lock of threadid:%d\n",pthread_self());
}
inline ~ThreadLock()
{
//printf("unlock of threadid:%d\n",pthread_self());
pthread_mutex_unlock(mutexPtr);
}
};
class CASFreeLock
{
private:
int* mutex;
int* lock;
int* unlock;
public:
explicit inline CASFreeLock(int& m, int& l, int& u)
{
mutex = &m;
lock = &l;
unlock = &u;
//printf("mutex:%d,lock:%d,unlock:%d\n",*mutex,*lock,*unlock);
while(!__sync_bool_compare_and_swap(mutex, *lock, 1))
{
usleep(100000);//sleep 10ms
}
}
inline ~CASFreeLock()
{
//printf("~mutex:%d,lock:%d,unlock:%d\n",*mutex,*lock,*unlock);
__sync_bool_compare_and_swap(mutex, *unlock, 0);
}
};
#endif
/*
* pthread_join使一个线程等待另一个线程结束。 代码中如果没有pthread_join主线程会很快结束从而使整个进程结束,
* 从而使创建的线程没有机会开始执行就结束。
* */
/*run:
* g++ threadsync.cpp -o threadcas
* ./threadcas.exe or ./threadcas
* */
运行结果如下,通过结果可以看出使用cas原子操作比线程加锁的速度提高了15倍左右:
Administrator@huihui ~/Linux/thread
$ ./threadcas.exe
thread lock->count:320000800000, time use=5102ms
cas lock free->count2:320000800000, time use=313ms
Administrator@huihui ~/Linux/thread
$ ./threadcas.exe
thread lock->count:320000800000, time use=5187ms
cas lock free->count2:320000800000, time use=339ms
Administrator@huihui ~/Linux/thread
$ ./threadcas.exe
thread lock->count:320000800000, time use=5120ms
cas lock free->count2:320000800000, time use=317ms