C ++内存顺序用于从多个线程一致地存储在原子变量中

问题描述

运行以下代码数百次,我希望打印的值始终为3,但似乎只有75%的时间是3。这可能意味着我对C ++中各种内存顺序的目的或原子操作的观点有误解。我的问题是:有没有办法保证以下程序的输出是可预测的?

#include <atomic>
#include <iostream>
#include <thread>
#include <vector>

int main () {
  std::atomic<int> cnt{0};
  auto f = [&](int n) {cnt.store(n,std::memory_order_seq_cst);};

  std::vector<std::thread> v;
  for (int n = 1; n < 4; ++n)
    v.emplace_back(f,n);

  for (auto& t : v)
    t.join();

  std::cout << cnt.load() << std::endl;
  return 0;
}

例如,这是运行100次后的输出统计信息:

$ clang++ -std=c++20 -Wall foo.cpp -pthread && for i in {1..100}; do ./a.out; done | sort | uniq -c
      2 1
     21 2
     77 3

解决方法

您观察到的与存储顺序正交。

调度程序不能保证具有相同优先级的线程的执行顺序。即使CPU处于空闲状态并且将线程分配给了不同的CPU,高速缓存未命中和锁争用也会使线程相对于其他CPU上的线程停滞。或者,如果CPU忙于运行具有相同或更高优先级的其他线程,那么您的新线程将不得不等到正在运行的线程耗尽其时间片或在内核中阻塞之后,调度程序很难预测到较早发生的事情。仅当您的系统具有一个CPU时,新线程才会以相对于彼此的预期顺序运行,因为它们将在一个CPU上形成一个队列。

在这里

std::memory_order_relaxed就足够了,因为您不需要在存储到cnt和存储/加载到其他非原子变量之间进行任何排序。 std::atomic始终是原子的,std::memory_order指定是否可以相对于std::atomic变量的加载或存储对其他非原子变量的加载和存储进行重新排序。