在实际的编程中,我们不免需要对变量进行加减和逻辑操作。由于对变量的加减和逻辑操作并不是原子操作,在单线程编程中这并不会有什么的影响;在多线程编程中这就会造成问题。
在多线程编程中,如果需要对同一变量进行访问。那么我们最先想到的可能是利用线程的同步机制(比如互斥量)制造出一片线程安全的区域,并在这片区域对变量进行访问。这似乎“合情合理”,确实这种做法是正确的,它的确能确保对变量访问的线程安全,但某些情况下,这种处理方式效率是低下的。
例如:在实际的项目中我们可能需要统计一整天的客户端的连接数,当一个客户端连接上服务器,我们需要对这个变量进行自增。这时候如果使用线程的同步机制来实现线程的安全,效率显然是低下的,事实上我们可能只需要对变量进行自增,但是使用线程的同步机制来对变量进行保护的开销远远大于自增操作。这时候我们就可以充分的利用编译器为我们提供的一些原子操作。
gcc从4.1.2提供了__sync_*系列的built-in函数,用于提供加减和逻辑运算的原子操作。这些函数如下所示:
//返回更新前的值,类似于后缀++
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, ...)
//返回更新后的值,类似于前缀++
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 == oldval,就将newval写入*ptr
bool __sync_bool_compare_and_swap (type *ptr, type oldval type newval, ...) //在相等并写入的情况下返回true
type __sync_val_compare_and_swap (type *ptr, type oldval type newval, ...) //返回操作之前的值
type __sync_lock_test_and_set (type *ptr, type value, ...) //将*ptr设为value并返回*ptr操作之前的值
void __sync_lock_release (type *ptr, ...) //将*ptr设为0
其中type可以是1,2,4,或8字节长度的int类型,即:
int8_t / uint8_t
int16_t / uint16_t
int32_t / uint32_t
int64_t / uint64_t
下面的例子是一个多线程的原子计数器。
#include <iostream>
#include <pthread.h>
using namespace std;
#define THREAD_NUM 2
int g_iSum = 0;
void *Func(void *pArg)
{
for (int i = 0; i < 10000; i++)
{
//g_iSum++; //这种方式在多处理器上运行出现结果不唯一
//用__sync_fetch_and_add进行处理能获得正确的结果
//与使用线程同步一样的效果,但效率上更高
__sync_fetch_and_add(&g_iSum, 1);
}
pthread_exit((void *)1);
}
int main(void)
{
int iRet;
pthread_t ThreadFuncId[THREAD_NUM];
for (int i = 0; i < THREAD_NUM; i++)
{
iRet = pthread_create(&ThreadFuncId[i], NULL, Func, NULL);
if (iRet != 0)
{
cout << "pthread_create Func return false!" << endl;
return -1;
}
}
for (int i = 0; i < THREAD_NUM; i++)
{
iRet = pthread_join(ThreadFuncId[i], NULL);
if (iRet != 0)
{
cout << "pthread_join Func return false!" << endl;
return -1;
}
}
cout << "g_iSum = " << g_iSum << endl;
return 0;
}