【问题标题】:What are some use cases for memory_order_relaxedmemory_order_relaxed 的一些用例是什么
【发布时间】:2014-06-22 15:13:25
【问题描述】:

C++ 内存模型具有宽松的原子性,它不对内存操作提供任何顺序保证。除了我在这里找到的 C 中的邮箱示例:

http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1525.htm

基于本文中的激励示例:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2153.pdf

我很好奇这种同步机制的其他用例。

【问题讨论】:

    标签: c++ memory-model


    【解决方案1】:

    在这种情况下,事件阅读器可以连接到 X11 套接字,其中事件的频率取决于用户操作(调整窗口大小、打字等)并且如果 GUI 线程的事件调度程序定期检查事件(例如,由于对于用户应用程序中的某些计时器事件)我们不想通过获取我们知道为空的共享事件队列上的锁来不必要地阻塞事件读取器线程。我们可以使用“dataReady”原子简单地检查是否有任何内容排队。这也称为“双重检查锁定”模式。

    namespace {
    std::mutex mutex;
    std::atomic_bool dataReady(false);
    std::atomic_bool done(false);
    std::deque<int> events; // shared event queue, protected by mutex
    }
    
    void eventReaderThread()
    {
        static int eventId = 0;
        std::chrono::milliseconds ms(100);
        while (true) {
            std::this_thread::sleep_for(ms);
            mutex.lock();
            eventId++; // populate event queue, e.g from pending messgaes on a socket
            events.push_back(eventId);
            dataReady.store(true, std::memory_order_release);
            mutex.unlock();
            if (eventId == 10) {
                done.store(true, std::memory_order_release);
                break;
            }
        }
    }
    
    void guiThread()
    {
        while (!done.load(std::memory_order_acquire)) {
            if (dataReady.load(std::memory_order_acquire)) { // Double-checked locking pattern
                mutex.lock();
                std::cout << events.front() << std::endl;
                events.pop_front();
                // If consumer() is called again, and producer() has not added new events yet,
                // we will see the value set via this memory_order_relaxed.
                // If producer() has added new events, we will see that as well due to normal
                // acquire->release.
                // relaxed docs say: "guarantee atomicity and modification order consistency"
                dataReady.store(false, std::memory_order_relaxed);
                mutex.unlock();
            }
        }
    }
    
    int main()
    {
        std::thread producerThread(eventReaderThread);
        std::thread consumerThread(guiThread);
        producerThread.join();
        consumerThread.join();
    }
    

    【讨论】:

      【解决方案2】:

      我在工作中经常看到的一个简单示例是统计计数器。如果你 想要计算事件发生的次数,但不需要任何类型的 除了使增量安全之外,跨线程同步,使用 memory_order_relaxed 有道理。

      static std::atomic<size_t> g_event_count_;
      
      void HandleEvent() {
        // Increment the global count. This operation is safe and correct even
        // if there are other threads concurrently running HandleEvent or
        // PrintStats.
        g_event_count_.fetch_add(1, std::memory_order_relaxed);
      
        [...]
      }
      
      void PrintStats() {
        // Snapshot the "current" value of the counter. "Current" is in scare
        // quotes because the value may change while this function is running.
        // But unlike a plain old size_t, reading from std::atomic<size_t> is
        // safe.
        const size_t event_count =
            g_event_count_.load(std::memory_order_relaxed);
      
        // Use event_count in a report.
        [...]
      }
      

      在这两种情况下,都不需要使用更强的内存顺序。一些 平台,这样做可能会对性能产生负面影响。

      【讨论】:

      • 在延迟计算某些东西的情况下使用宽松的内存顺序是否也合适,并且计算不止一次会稍微低效但无害?如果一个值会被读取数百万次,即使每次读取成本的微小降低也足以弥补一些冗余计算的成本。
      • 这对我来说似乎很好,但您必须非常小心,不要尝试使用该值进行同步。例如,如果您计算一个结构,然后尝试将std::atomic&lt;Struct*&gt;std::memory_order_relaxed 一起使用,那么您将度过一段糟糕的时光,因为您没有确保其他线程在写入之前看到初始化结构的写入设置指针。
      • 好的,所以你有作家可以原子地增加一个计数器。但最终你会想在某处阅读计数器。就像您示例中的 PrintStats() 一样。那么这仅适用于您的计数增量不一定要立即传播吗?当您使用 std::memory_order_relaxed 读取计数器时,您是否有可能读取过时的 g_event_count?
      • 不,这会由于data race 而导致未定义的行为。基于代码编译器实际生成的可能结果是丢失增量,但理论上任何事情都可能发生。
      • @HCSF:如果您正在编写汇编代码,那么您可以了解架构的作用,但如果编译器正在为您编写汇编,则不会。 There is no such thing as a benign data race。在这种情况下,即使忘记了数据竞争,您仍然会丢失增量:编译器可能会生成一个幼稚的“加载,加 1,存储”,这不是原子的。
      猜你喜欢
      • 2019-06-04
      • 2010-10-21
      • 2019-10-29
      • 1970-01-01
      • 2011-06-02
      • 2010-12-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多