宽松模型

此模型下,单个线程内的原子操作都是顺序执行的,不允许指令重排,但是不同线程间原子操作的顺序是任意的。

在模型中,限制了线程间的操作顺序,如果某个线程需要修改某个值,但另一个线程会对该值的某次操作产生一俩(后者依赖前者)。

则A释放(release),B消费(consume)

// 初始化为 nullptr 防止 consumer 线程从野指针进行读取
std::atomic<int*> ptr(nullptr);
int v;
std::thread producer([&]() {
  int* p = new int(42);
  v = 1024;
  ptr.store(p, std::memory_order_release);
});
std::thread consumer([&]() {
  int* p;
  while(!(p = ptr.load(std::memory_order_consume)));
  std::cout << "p: " << *p << std::endl;
  std::cout << "v: " << v << std::endl;
});
producer.join();
consumer.join();

释放/获取模型

在释放和获取之间规定了时序。

release确保了后向屏障(backward):它之前的写行为不会发生在release之后

acq_rel结合两者,确定了一个内存屏障区间,使得当前线程对于内存的读写不会被重排并越过此操作的前后。

顺序模型

release-consume模型与release-acquire模型对比

release-consume模型和release-acquire模型是并发编程中的两种内存模型,用于确保多线程操作共享数据时的正确性。它们之间的主要异同点如下:

  1. 异同点:

    • release-acquire模型(也称为释放-获取模型):在该模型中,一个线程释放(release)一个变量,而另一个线程获取(acquire)该变量。通过释放和获取操作,确保在一个线程读取(获取)该变量之前,先于其之前的写操作(释放)已经完成。
    • release-consume模型(也称为释放-消费模型):在该模型中,一个线程释放(release)一个变量,而另一个线程消费(consume)该变量。通过释放和消费操作,确保在一个线程消费(读取)该变量时,先于其之前的写操作(释放)已经完成。
      异同点是指 release-acquire 模型强调对共享数据的获取操作,而 release-consumer 模型则强调对共享数据的消费操作。
  2. 例子:

    一个简单的例子是使用队列来实现线程间的通信。假设有一个生产者线程不断向队列中添加数据(写操作),而消费者线程从队列中提取数据并进行处理(读操作)。我们来看看这两种模型在这种情况下的应用:

    • release-acquire模型:在生产者线程添加数据到队列之后,通过使用 release 释放该变量,确保在消费者线程获取队列数据之前,生产者线程先于其之前的写操作已经完成。
    • release-consume模型:在生产者线程添加数据到队列之后,通过使用 release 释放该变量,确保在消费者线程消费队列数据时,生产者线程先于其之前的写操作已经完成。

总体而言,虽然异同点是指重点不同,但这两种模型都旨在确保内存操作的正确顺序,以避免共享数据的竞争条件和数据不一致等问题。具体使用哪种模型取决于具体的应用场景和需求。