memory_order_relaxed的一些用例是什么

What are some use cases for memory_order_relaxed

本文关键字:是什么 order relaxed memory      更新时间:2023-10-16

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

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

我在工作中经常看到的一个简单例子是统计计数器。如果你想要统计事件发生的次数,但不需要任何类型的除了确保增量安全之外,还可以跨线程同步,使用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.
  [...]
}

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

在这种情况下,事件读取器可以连接到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();
}