• muduo笔记 原子类AtomicIntegerT<T>


    原子操作

    C++中的原子操作:
    1)C++11以后,提供st::atomic可以实现T类型数据的原子操作,主要包括:初始化、读取值、写值、自增自减(i.e. 前置或后置++)等。
    2)对于C++11以前的版本,需要用GCC编译器提供的原子操作接口,实现原子操作。

    AtomicIntegerT模板类

    muduo产生与C++11流行之前,因此用了第二种方案。实际上,Linux下面,C++11中的std::stomic实现,也是用的第一种方案实现的。
    自定义AtomicIntegerT模板:

    /* GCC atomic operation see
    * https://www.cnblogs.com/the-tops/p/6347584.html
    * https://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync
    */
    template<typename T>
    class AtomicIntegerT : noncopyable
    {
    public:
        AtomicIntegerT()
        :value_(0)
        { }
    
        T get()
        {
            // CAS(compare and swap)
            // in gcc >= 4.7: __atomic_load_n(&value, __ATOMIC_SEQ_CST)
            return __sync_val_compare_and_swap(&value_, 0, 0);
        }
        // 先fetch获取值(value_),然后再add加x
        T getAndAdd(T x)
        {
            // in gcc >= 4.7: __atomic_fetch_add(&value_, x, __ATOMIC_SEQ_CST)
            return __sync_fetch_and_add(&value_, x);
        }
        // getAndAdd()中已经用原子操作改变了value_值,这里是通过值传递方式返回 old value_ + x
        T addAndGet(T x)
        {
            return getAndAdd(x) + x;
        }
        // 前缀式递增,相当于++value_
        T incrementAndGet()
        {
            return addAndGet(1);
        }
        // 前缀式递减,相当于--value_
        T decrementAndGet()
        {
            return addAndGet(-1);
        }
        // 求和,相当于value_ = value_ + x
        void add(T x)
        {
            getAndAdd(x);
        }
        // 单纯递增,不关心返回值
        void increment()
        {
            incrementAndGet();
        }
        // 单纯递减,不关心返回值
        void decrement()
        {
            decrementAndGet();
        }
        // 先fetch old value_,然后set value_ = newValue
        T getAndSet(T newValue)
        {
            // in gcc >= 4.7: __atomic_exchange_n(&value_, newValue, __ATOMIC_SEQ_CST)
            return __sync_lock_test_and_set(&value_, newValue);
        }
    
    private:
        volatile T value_;
    };
    

    为了使用方便,同时避免重复命名、实例化、甚至编译,使用AtomicIntegerT包装int32_t, int64_t,然后重定义,我们在.h文件中声明这2个

    // namespace muduo::detail
    typedef detail::AtomicIntegerT<int32_t> AtomicInt32;
    typedef detail::AtomicIntegerT<int64_t> AtomicInt64;
    

    单元测试

    主要针对AtomicInt64、AtomicInt32 这2个常用的、具体的类型,进行测试。

    方法是通过构造实例对象后,调用成员函数get()、getAndAdd()、addAndGet()、incrementAndGet()、decrementAndGet(),对原子对象进行修改,然后根据返回值判断值是否为预期值。

    // AtomicInt64 的测试
    {
        AtomicInt64 a0;
        assert(a0.get() == 0);
        assert(a0.getAndAdd(1) == 0);
        assert(a0.get() == 1);
        assert(a0.addAndGet(2) == 3);
        assert(a0.get() == 3);
        assert(a0.incrementAndGet() == 4);
        assert(a0.get() == 4);
        a0.increment();
        assert(a0.get() == 5);
        assert(a0.addAndGet(-3) == 2);
        assert(a0.getAndSet(100) == 2);
        assert(a0.get() == 100);
    }
    
    // AtomicInt32 的测试类同,只需要修改a0的类型为AtomicInt32即可,具体代码略
    ... 
    

    知识点

    gcc原子操作

    原子自增操作

    type __sync_fetch_and_add(type *ptr, type value)
    

    原子比较和交换(设置)操作

    type __sync_val_compare_and_swap(type *ptr, type oldval type newval)
    bool __sync_bool_compare_and_swap(type *ptr, type oldval type newval)
    

    原子赋值操作

    type __sync_lock_test_and_set(type *ptr, type value)
    

    使用这些原子操作,编译时需要加-march=cpu-type (CPU体系结构=CPU类型,可以指定为native)

    volatile关键字

    确保本条指令不会因为编译器的优化而省略,而且要求每次从内存直接读值,而不是读高速cache中的备份。

    volatile T value_;
    

    参考

    https://www.cnblogs.com/the-tops/p/6347584.html
    https://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync
    https://coolshell.cn/articles/8239.html

  • 相关阅读:
    雷林鹏分享:Lua if 嵌套语句
    雷林鹏分享:Lua if...else 语句
    雷林鹏分享:Lua if 语句
    debug 使用lldb
    mysql高负载的问题排查
    一个NULL引发的血案
    画椭圆型
    异常分析
    go 语言的库文件放在哪里?如何通过nginx代理后还能正确获取远程地址
    defer 要放在return之前
  • 原文地址:https://www.cnblogs.com/fortunely/p/15943322.html
Copyright © 2020-2023  润新知