C/C++编程:__sync_系列函数实现无锁原子类

1059 篇文章 280 订阅

函数声明

在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并返回true
  • type __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);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值