众所周知,多线程下计数存在着计数不正确的问题。这个问题的根源在于多个线程对同一个变量可以同时访问(修改)。这样就造成了修改后的结果不一致。
首先在这里先强调一点,volatile 关键字并不能提供多线程安全访问。因为有volatie修饰的变量,每次操作时遵循下面动作:
从内存取值 ---> 放入寄存器 ---> 操作 --->写回内存
这几个步骤不是原子的操作在任意两个步骤之间都可能被其他的线程中断,所以不是线程安全。
多线程访问同一个变量,大多数程序员最先想到的就是利用锁制造处一片线程安全的区域在该区域内,做线程安全的操作。
原子计数操作是加锁的多倍效率,用原子操作可以省去很多时间和代码。这里说明一下各个平台的原子操作。1.Linux平台的原子操作
gcc从4.1.2提供了__sync_*系列的built-in函数,用于提供加减和逻辑运算的原子操作。
可以对1,2,4或8字节长度的数值类型或指针进行原子操作,其声明如下
type __sync_fetch_and_add (type *ptr, type value, ...)
type __sync_fetch_and_sub (type *ptr, type value, ...)
type __sync_fetch_and_or (type *ptr, type value, ...)
type __sync_fetch_and_and (type *ptr, type value, ...)
type __sync_fetch_and_xor (type *ptr, type value, ...)
type __sync_fetch_and_nand (type *ptr, type value,...)
{ tmp =*ptr; *ptr op= value; return tmp; }
{ tmp =*ptr; *ptr = ~tmp & value; return tmp; } // nand
type __sync_add_and_fetch (type *ptr, type value, ...)
type __sync_sub_and_fetch (type *ptr, type value, ...)
type __sync_or_and_fetch (type *ptr, type value, ...)
type __sync_and_and_fetch (type *ptr, type value, ...)
type __sync_xor_and_fetch (type *ptr, type value, ...)
type __sync_nand_and_fetch (type *ptr, type value,...)
{ *ptr op=value; return *ptr; }
{ *ptr =~*ptr & value; return *ptr; } //nand
这两组函数的区别在于第一组返回更新前的值,第二组返回更新后的值。
简单实例程序:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
static int count = 0;
void *test_func(void *arg)
{
int i=0;
for(i=0;i<2000;++i)
{
__sync_fetch_and_add(&count,1);
}
return NULL;
}
int main(int argc, const char *argv[])
{
pthread_tid[20];
int i =0;
for(i=0;i<8;++i){
pthread_create(&id[i],NULL,test_func,NULL);
}
for(i=0;i<8;++i){
pthread_join(id[i],NULL);
}
printf("%d\n",count);
return 0;
}
基于GCC编译器,我们可以自己定义实现一个多线程安全的原子计数器。实现代码如下
my_stomic.hpp
-
最后 在用gcc编译的时候要加上选项 -march=i686哟#ifndefMY_ATOMIC_H #defineMY_ATOMIC_H class my_atmic { public: volatile int m_coun; explicit my_atmic(int cunn) :m_coun(cunn) {} ~my_atmic() {} my_atmic& operator++() { __sync_fetch_and_add(&m_coun,1); return *this; } my_atmic& operator--() { __sync_fetch_and_sub(&m_coun,1); return *this; } my_atmic operator ++(int) { my_atmic ret(this->m_coun); ++*this; return ret; } my_atmic operator--(int) { my_atmic ret(this->m_coun); --*this; return ret; } my_atmic& operator+=(constmy_atmic& m) { __sync_fetch_and_add(&m_coun,m.m_coun); return *this; } my_atmic& operator +=(int m) { __sync_fetch_and_add(&m_coun,m); return *this; } my_atmic& operator-=(constmy_atmic& m) { __sync_fetch_and_sub(&m_coun,m.m_coun); return *this; } my_atmic& operator -=(int m) { __sync_fetch_and_sub(&m_coun,m); return *this; } my_atmic& operator=(const my_atmic&m) { this->m_coun = m.m_coun; return *this; } my_atmic& operator=(int m) { this->m_coun = m; return *this; } private: my_atmic(){} my_atmic(const my_atmic& ); }; #endif //MY_ATOMIC_H
-