代码之家  ›  专栏  ›  技术社区  ›  meguli

实现原子的不同方法*=

  •  4
  • meguli  · 技术社区  · 7 年前

    通过一本书的学习,它解释了如何实现更复杂的操作,比如 operator* 对于 std::atomic<T> compare_exchange_weak 我想我明白这是怎么回事。现在,我自己实现了一些东西,看看。

    #include <type_traits>
    #include <atomic>
    #include <iostream>
    
    /*template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
    std::atomic<T>& operator*=(std::atomic<T>& t1, T t2) {
        T expected = t1.load();
        while(!t1.compare_exchange_weak(expected, expected * t2))
        {}
        return t1;
    }*/
    
    template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
    std::atomic<T>& operator*=(std::atomic<T>& t1, T t2) {
        T expected = t1.load();
        t1.compare_exchange_weak(expected, expected * t2);
        return t1;
    }
    
    int main() {
        std::atomic<int> t1 = 5;
        std::atomic<int> t2;
        t2 = (t1 *= 5).load();
    
        std::cout << "Atomic t1: " << t1 << "\n";
        std::cout << "Atomic t2: " << t2 << "\n";
    } 
    

    我有两个版本的代码,书的版本被注释掉了。我不明白为什么我要在繁忙的循环中等待执行原子 compare_exchange

    lock cmpxchg dword ptr [rsp + 8], ecx
    

    看起来和我很像。那么,为什么我需要一个像书中那样的等待循环来使这个东西原子化呢?我的版本是不是也很好,原子性的工作?

    1 回复  |  直到 7 年前
        1
  •  6
  •   rmm19433    7 年前

    想象一下你打电话给 load compare_exchange_weak 值被另一个线程更改。 expected

    比较弱的交换 工作如下:

    表示(因为C++ 20)的*与预期的,如果 它们是按位相等的,用所需的替换前者(执行 读修改写操作)。否则,加载存储的实际值 cppreference

    基于上述描述 t1 通过循环确保更新 t1级 存储乘法结果或更新 预期


    编辑: 您可以通过模拟并发访问来“尝试”。在交换结果之前,另一个线程进入并更改原子的值。在下面 比较弱的交换 预期

    +----------- Thread 1 -----------+---------- Thread 2 ----------+
    | ex = t1.load()                 |                              |
    |                                | t1.store(42)                 |
    | t1.cmp_xchg_w(ex, ex * t2)     |                              |
    

    这段代码模拟并发访问并让单个线程休眠。

    #include <type_traits>
    #include <atomic>
    #include <iostream>
    #include <chrono>
    #include <thread>
    
    template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
    std::atomic<T>& operator*=(std::atomic<T>& t1, T t2) {
        using namespace std::chrono_literals;
        T expected = t1.load();
        std::this_thread::sleep_for(400ms);
        t1.compare_exchange_weak(expected, expected * t2);
        return t1;
    }
    
    int main() {
        std::atomic<int> t1 = 5;
        std::atomic<int> t2;
        std::thread th1([&](){
            t2 = (t1 *= 5).load();
        });
        std::thread th2([&](){
            using namespace std::chrono_literals;
            std::this_thread::sleep_for(100ms);
            t1.store(8);
        });
    
        th1.join();
        th2.join();
    
        std::cout << "Atomic t1: " << t1 << "\n";
        std::cout << "Atomic t2: " << t2 << "\n";
    }