函数声明
在gcc4.1.2版本之后,对X86和X86_64支持内置的原子操作。就会说,不需要引入第三方库(比如pthread)的锁保护,就可以对1、2、4、8字节的数值或者指针类型,进行原子的加/减/或/异或等操作。有了这套内置的原子操作,写程序就会方便很多。根据Gcc手册中,章节内容,将__sync_系列17个函数声明整理简化如下:
type __sync_fetch_and_add (type *ptr, type value, ...)
: 将value加到*ptr
上,结果更新到*ptr,并返回操作之前*ptr
的值type __sync_fetch_and_sub (type *ptr, type value, ...)
:从*ptr
减去value,结果更新到*ptr
,并返回操作之前*ptr
的值type __sync_fetch_and_or (type *ptr, type value, ...)
: 将*ptr
与value相或,结果更新到*ptr
, 并返回操作之前*ptr
的值type __sync_fetch_and_and (type *ptr, type value, ...)
: 将*ptr
与value相与,结果更新到*ptr
,并返回操作之前*ptr
的值type __sync_fetch_and_xor (type *ptr, type value, ...)
:将*ptr
与value异或,结果更新到*ptr
,并返回操作之前*ptr
的值type __sync_fetch_and_nand (type *ptr, type value, ...)
:将*ptr
取反后,与value相与,结果更新到*ptr
,并返回操作之前*ptr
的值type __sync_add_and_fetch (type *ptr, type value, ...)
: 将value加到*ptr
上,结果更新到*ptr
,并返回操作之后新*ptr
的值type __sync_sub_and_fetch (type *ptr, type value, ...)
: 从*ptr
减去value,结果更新到*ptr
,并返回操作之后新*ptr
的值type __sync_or_and_fetch (type *ptr, type value, ...)
: 将*ptr
与value相或, 结果更新到*ptr
,并返回操作之后新*ptr
的值type __sync_and_and_fetch (type *ptr, type value, ...)
: 将*ptr
与value相与,结果更新到*ptr
,并返回操作之后新*ptr
的值
-type __sync_xor_and_fetch (type *ptr, type value, ...)
: 将*ptr
与value异或,结果更新到*ptr
,并返回操作之后新*ptr
的值type __sync_nand_and_fetch (type *ptr, type value, ...)
:将*ptr
取反后,与value相与,结果更新到*ptr
,并返回操作之后新*ptr的值bool __sync_bool_compare_and_swap (type *ptr, type oldval type newval, ...)
:比较*ptr
与oldval的值,如果两者相等,则将newval更新到*ptr
并返回truetype __sync_val_compare_and_swap (type *ptr, type oldval type newval, ...)
: 比较*ptr
与oldval的值,如果两者相等,则将newval更新到*ptr
并返回操作之前*ptr
的值__sync_synchronize (...)
:发出完整内存栅栏type __sync_lock_test_and_set (type *ptr, type value, ...)
:将value写入*ptr
,对*ptr
加锁,并返回操作之前*ptr
的值。即,try spinlock语义void __sync_lock_release (type *ptr, ...)
: 将0写入到*ptr
,并对*ptr
解锁。即,unlock spinlock语义
在没有引入sync系列函数之前原子类应该怎么写
#include <iostream>
#include <map>
#include <assert.h>
struct ACL_ATOMIC{
void *value;
pthread_mutex_t lock;
};
/**
* 创建原子对象
* @return {ACL_ATOMIC*} 返回新创建的对象
*/
ACL_ATOMIC * acl_atomic_new(){
ACL_ATOMIC *self = (ACL_ATOMIC *) malloc(sizeof(ACL_ATOMIC));
pthread_mutex_init(&self->lock, NULL);
self->value = NULL;
return self;
}
/**
* 释放原子对象
* @param self {ACL_ATOMIC*} 原子对象
*/
void acl_atomic_free(ACL_ATOMIC *self){
self->value = NULL;
pthread_mutex_destroy(&self->lock);
free(self);
}
/**
* 将指定对象与原子对象绑定,以便于对该对象进行原子操作
* @param self {ACL_ATOMIC*} 原子对象
* @param value {void*} 被操作的对象,通过原子对象实现对该对象的原子操作
*/
void acl_atomic_set(ACL_ATOMIC *self, void *value)
{
pthread_mutex_lock(&self->lock);
self->value = value;
pthread_mutex_unlock(&self->lock);
}
/**
* 比较并交换对象,当原子对象绑定的对象与给定比较对象相同时才设置新对象且
* 返回之前绑定的对象
* @param self {ACL_ATOMIC*} 原子对象
* @param cmp {void*} 待比较对象指针
* @param value {void*} 当原子对象与待比较对象相同时会将该对象与原子对象绑定
* @return {void*} 返回原子对象之前绑定的对象
*/
void *acl_atomic_cas(ACL_ATOMIC *self, void *cmp, void *value)
{
void *old;
pthread_mutex_lock(&self->lock);
old = self->value;
if (self->value == cmp) // 当前原子类存储的内存地址是不是cmp这个内存的地址
self->value = value;
pthread_mutex_unlock(&self->lock);
return old;
}
/**
* 将原子对象与新对象进行绑定,并返回之前绑定的对象
* @param self {ACL_ATOMIC*} 原子对象
* @param value {void*} 将被绑定的新对象
* @return {void*} 返回之前绑定的对象
*/
void *acl_atomic_xchg(ACL_ATOMIC *self, void *value){
void *old;
pthread_mutex_lock(&self->lock);
old = self->value;
self->value = value;
pthread_mutex_unlock(&self->lock);
return old;
}
/**
* 当调用 acl_atomic_set 绑定的对象为数值对象时,可以调用此函数设置被绑定对象
* 的长整数值
* @param self {ACL_ATOMIC*} 原子对象
* @param n {long long} 被原子对象所绑定的对象将被赋值为此值
*/
void acl_atomic_int64_set(ACL_ATOMIC *self, long long n){
pthread_mutex_lock(&self->lock);
*((long long *)self->value) = n;
pthread_mutex_unlock(&self->lock);
}
/**
* 先获得数值对象所存储的整数值,然后再增加指定的值存储于该数值对象中
* @param self {ACL_ATOMIC*} 原子对象
* @param n {long long} 增加值
* @return {long long} 返回增加之前数据数值对象的值
*/
long long acl_atomic_int64_fetch_add(ACL_ATOMIC *self, long long n){
pthread_mutex_lock(&self->lock);
long long v = *(long long *) self->value;
*(long long *) self->value = v + n;
pthread_mutex_unlock(&self->lock);
return v;
}
/**
* 对数据对象存储的值增加指定的值,并返回结果值
* @param self {ACL_ATOMIC*} 原子对象
* @param n {long long} 增加值
* @return {long long} 返回增加之后的值
*/
long long acl_atomic_int64_add_fetch(ACL_ATOMIC *self, long long n){
pthread_mutex_lock(&self->lock);
long long v = *((long long *)self->value) + n;
*((long long *)self->value) = v;
pthread_mutex_unlock(&self->lock);
return v;
}
/**
* 比较并交换整数值,当原子对象存储的整数值与给定比较整数值相同时才设置新整数
* 值且返回之前存储的整数值
* @param self {ACL_ATOMIC*} 原子对象
* @param cmp {long long} 待比较整数值
* @param n {long long} 当原子对象与待比较整数值相同时会将原子对象设置为此值
* @return {long long} 返回原子对象之前存储的整数值
*/
long long acl_atomic_int64_cas(ACL_ATOMIC *self, long long cmp, long long n){
pthread_mutex_lock(&self->lock);
long long old = *(long long *) self->value;
if (old == cmp)
*((long long *) self->value) = n;
pthread_mutex_unlock(&self->lock);
return old;
}
int main() {
ACL_ATOMIC * atomic = acl_atomic_new();
int addr = 5;
acl_atomic_set(atomic, &addr);
int cmp = 5; int newaddr = 11;
int *old = (int *)acl_atomic_cas(atomic, &cmp, &newaddr); // (因为newval和self.value的内存位置不同), 所以atomic操作的内存还是val
assert(old);
printf("%d\n", *(int *)atomic->value);
old = (int *)acl_atomic_cas(atomic, &addr, &newaddr); // (因为val和self.value的内存位置同), 所以atomic操作的内存变成了newval
assert(old);
printf("%d", *(int *)atomic->value);
acl_atomic_free(atomic);
}
sync之后实现无锁原子类
#include <iostream>
#include <map>
#include <assert.h>
# if defined(__GNUC__) && (__GNUC__ >= 4)
# define HAS_ATOMIC
# endif
struct ACL_ATOMIC{
void *value;
#ifndef HAS_ATOMIC
pthread_mutex_t lock;
#endif
};
/**
* 创建原子对象
* @return {ACL_ATOMIC*} 返回新创建的对象
*/
ACL_ATOMIC * acl_atomic_new(){
ACL_ATOMIC *self = (ACL_ATOMIC *) malloc(sizeof(ACL_ATOMIC));
#ifndef HAS_ATOMIC
pthread_mutex_init(&self->lock, NULL);
#endif
self->value = NULL;
return self;
}
/**
* 释放原子对象
* @param self {ACL_ATOMIC*} 原子对象
*/
void acl_atomic_free(ACL_ATOMIC *self){
self->value = NULL;
#ifndef HAS_ATOMIC
pthread_mutex_destroy(&self->lock);
#endif
free(self);
}
/**
* 将指定对象与原子对象绑定,以便于对该对象进行原子操作
* @param self {ACL_ATOMIC*} 原子对象
* @param value {void*} 被操作的对象,通过原子对象实现对该对象的原子操作
*/
void acl_atomic_set(ACL_ATOMIC *self, void *value)
{
#ifndef HAS_ATOMIC
pthread_mutex_lock(&self->lock);
self->value = value;
pthread_mutex_unlock(&self->lock);
#else
(void) __sync_lock_test_and_set(&self->value, value);
#endif
}
/**
* 比较并交换对象,当原子对象绑定的对象与给定比较对象相同时才设置新对象且
* 返回之前绑定的对象
* @param self {ACL_ATOMIC*} 原子对象
* @param cmp {void*} 待比较对象指针
* @param value {void*} 当原子对象与待比较对象相同时会将该对象与原子对象绑定
* @return {void*} 返回原子对象之前绑定的对象
*/
void *acl_atomic_cas(ACL_ATOMIC *self, void *cmp, void *value)
{
#ifndef HAS_ATOMIC
void *old;
pthread_mutex_lock(&self->lock);
old = self->value;
if (self->value == cmp) // 当前原子类存储的内存地址是不是cmp这个内存的地址
self->value = value;
pthread_mutex_unlock(&self->lock);
return old;
#else
return __sync_val_compare_and_swap(&self->value, cmp, value);
#endif
}
/**
* 将原子对象与新对象进行绑定,并返回之前绑定的对象
* @param self {ACL_ATOMIC*} 原子对象
* @param value {void*} 将被绑定的新对象
* @return {void*} 返回之前绑定的对象
*/
void *acl_atomic_xchg(ACL_ATOMIC *self, void *value){
#ifndef HAS_ATOMIC
void *old;
pthread_mutex_lock(&self->lock);
old = self->value;
self->value = value;
pthread_mutex_unlock(&self->lock);
return old;
#else
return __sync_lock_test_and_set(&self->value, value);
#endif
}
/**
* 当调用 acl_atomic_set 绑定的对象为数值对象时,可以调用此函数设置被绑定对象
* 的长整数值
* @param self {ACL_ATOMIC*} 原子对象
* @param n {long long} 被原子对象所绑定的对象将被赋值为此值
*/
void acl_atomic_int64_set(ACL_ATOMIC *self, long long n){
#ifndef HAS_ATOMIC
pthread_mutex_lock(&self->lock);
*((long long *)self->value) = n;
pthread_mutex_unlock(&self->lock);
#else
(void) __sync_lock_test_and_set((long long *) self->value, n);
#endif
}
/**
* 先获得数值对象所存储的整数值,然后再增加指定的值存储于该数值对象中
* @param self {ACL_ATOMIC*} 原子对象
* @param n {long long} 增加值
* @return {long long} 返回增加之前数据数值对象的值
*/
long long acl_atomic_int64_fetch_add(ACL_ATOMIC *self, long long n){
#ifndef HAS_ATOMIC
pthread_mutex_lock(&self->lock);
long long v = *(long long *) self->value;
*(long long *) self->value = v + n;
pthread_mutex_unlock(&self->lock);
return v;
#else
return (long long) __sync_fetch_and_add((long long *) self->value, n);
#endif
}
/**
* 对数据对象存储的值增加指定的值,并返回结果值
* @param self {ACL_ATOMIC*} 原子对象
* @param n {long long} 增加值
* @return {long long} 返回增加之后的值
*/
long long acl_atomic_int64_add_fetch(ACL_ATOMIC *self, long long n){
#ifndef HAS_ATOMIC
pthread_mutex_lock(&self->lock);
long long v = *((long long *)self->value) + n;
*((long long *)self->value) = v;
pthread_mutex_unlock(&self->lock);
return v;
#else
return (long long) __sync_add_and_fetch((long long *) self->value, n);
#endif
}
/**
* 比较并交换整数值,当原子对象存储的整数值与给定比较整数值相同时才设置新整数
* 值且返回之前存储的整数值
* @param self {ACL_ATOMIC*} 原子对象
* @param cmp {long long} 待比较整数值
* @param n {long long} 当原子对象与待比较整数值相同时会将原子对象设置为此值
* @return {long long} 返回原子对象之前存储的整数值
*/
long long acl_atomic_int64_cas(ACL_ATOMIC *self, long long cmp, long long n){
#if !defined(HAS_ATOMIC)
pthread_mutex_lock(&self->lock);
long long old = *(long long *) self->value;
if (old == cmp)
*((long long *) self->value) = n;
pthread_mutex_unlock(&self->lock);
return old;
#else
return (long long) __sync_val_compare_and_swap(
(long long*) self->value, cmp, n);
#endif
}
int main() {
ACL_ATOMIC * atomic = acl_atomic_new();
int addr = 5;
acl_atomic_set(atomic, &addr);
int cmp = 5; int newaddr = 11;
int *old = (int *)acl_atomic_cas(atomic, &cmp, &newaddr); // (因为newval和self.value的内存位置不同), 所以atomic操作的内存还是val
assert(old);
printf("%d\n", *(int *)atomic->value);
old = (int *)acl_atomic_cas(atomic, &addr, &newaddr); // (因为val和self.value的内存位置同), 所以atomic操作的内存变成了newval
assert(old);
printf("%d", *(int *)atomic->value);
acl_atomic_free(atomic);
}