You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于C++ memory_order_seq_cst的内存序保证与死锁问题问询

C++ atomic memory_order_seq_cst与死锁问题分析

场景代码

#include <atomic>
#include <thread>
#include <cstdint>

void race_test() {

  std::atomic<std::size_t> data = 0;
  std::atomic_bool flag = false;

  constexpr std::size_t REP_COUNT = 1000;

  std::thread inc_thread{[&] {
    for (std::size_t i = 0; i != REP_COUNT; ++i) {
      data.fetch_add(1, std::memory_order_relaxed);
      flag.store(true, std::memory_order_release);
      flag.notify_one();
    }
  }};

  std::thread dec_thread{[&] {
    for (std::size_t i = 0; i != REP_COUNT; ++i) {
      while (data.load(std::memory_order_relaxed) == 0) {
        flag.wait(false, std::memory_order_acquire);
        flag.store(false, std::memory_order_relaxed);
        std::atomic_thread_fence(std::memory_order_seq_cst);
      }
      data.fetch_sub(1, std::memory_order_relaxed);
    }
  }};

  inc_thread.join();
  dec_thread.join();
}

int main()
{
  for (std::size_t i = 0; i != 1000; ++i)
    race_test();
}

核心问题

理想流程是生产者(inc_thread)递增data后设置flag并通知消费者(dec_thread);消费者在data为0时等待flag,清空flag后处理data。当前代码加seq_cst栅栏可运行,但存在以下疑问:

  • 为何memory_order_acq_rel栅栏无法替代seq_cst?
  • 移除栅栏但将flag.store(false)改为memory_order_seq_cst是否合规?
  • seq_cst是真的保证无死锁,还是依赖x86平台特性?
  • 如何修改代码确保符合标准且无死锁?

问题分析与解答

死锁根源

移除栅栏后偶发死锁,本质是消费者的flag.store(false)可能在生产者的flag.notify_one()之前执行,导致通知被覆盖:生产者设置flag=true后,消费者抢先将flag重置为false,后续生产者的notify_one()无法唤醒已进入等待的消费者,最终消费者因生产者完成所有循环而永久等待。

1. 为何acq_rel栅栏无法替代seq_cst?

memory_order_acq_rel仅保证当前线程内的内存操作顺序(栅栏前的存储不后移,栅栏后的加载不前移),但不强制全局总序。在弱内存模型平台(如ARM、PowerPC),不同线程可能看到操作顺序不一致:消费者的flag.store(false)可能被其他线程视为早于生产者的notify_one()执行,导致通知丢失。

而memory_order_seq_cst会建立全局统一的操作总序,所有线程看到的seq_cst操作顺序完全一致,确保生产者的flag.store(true)→notify_one()的顺序不会被消费者的flag.store(false)打乱,从根源避免通知覆盖。

2. 将flag.store(false)改为seq_cst是否合规?

完全合规。seq_cst是release的超集,既保证当前线程内的顺序约束,又参与全局总序。将flag.store(false)设为seq_cst后,该操作不会被重排到之前的data.load()之前,同时与生产者的flag.store(true, release)形成可靠同步,有效避免死锁。

3. seq_cst是否依赖x86平台特性?

seq_cst是C标准明确规定的内存顺序,不依赖x86的强内存模型。x86默认禁止大部分内存重排,使得非seq_cst操作也能表现出类似seq_cst的效果,但在弱内存模型平台(如ARM),若无seq_cst约束,内存重排会直接导致死锁。seq_cst的全局总序保证在任何符合C标准的平台上都能生效。

4. 符合标准的无死锁修改方案

方案一:优化flag同步逻辑

保留flag机制,用seq_cst存储替代栅栏,简化代码且保证跨平台可靠性:

#include <atomic>
#include <thread>
#include <cstdint>

void race_test() {

  std::atomic<std::size_t> data = 0;
  std::atomic_bool flag = false;

  constexpr std::size_t REP_COUNT = 1000;

  std::thread inc_thread{[&] {
    for (std::size_t i = 0; i != REP_COUNT; ++i) {
      data.fetch_add(1, std::memory_order_relaxed);
      flag.store(true, std::memory_order_seq_cst);
      flag.notify_one();
    }
  }};

  std::thread dec_thread{[&] {
    for (std::size_t i = 0; i != REP_COUNT; ++i) {
      while (data.load(std::memory_order_relaxed) == 0) {
        flag.wait(false, std::memory_order_acquire);
        flag.store(false, std::memory_order_seq_cst);
      }
      data.fetch_sub(1, std::memory_order_relaxed);
    }
  }};

  inc_thread.join();
  dec_thread.join();
}

int main()
{
  for (std::size_t i = 0; i != 1000; ++i)
    race_test();
}

方案二:移除flag,直接用data作为通知对象

更简洁的方案是利用data自身的变化作为通知条件,彻底避免flag覆盖问题:

#include <atomic>
#include <thread>
#include <cstdint>

void race_test() {

  std::atomic<std::size_t> data = 0;

  constexpr std::size_t REP_COUNT = 1000;

  std::thread inc_thread{[&] {
    for (std::size_t i = 0; i != REP_COUNT; ++i) {
      auto old_val = data.fetch_add(1, std::memory_order_release);
      if (old_val == 0) {
        // 仅当data从0变为1时通知,避免无效重复通知
        data.notify_one();
      }
    }
  }};

  std::thread dec_thread{[&] {
    for (std::size_t i = 0; i != REP_COUNT; ++i) {
      while (data.load(std::memory_order_acquire) == 0) {
        data.wait(0, std::memory_order_acquire);
      }
      data.fetch_sub(1, std::memory_order_release);
    }
  }};

  inc_thread.join();
  dec_thread.join();
}

int main()
{
  for (std::size_t i = 0; i != 1000; ++i)
    race_test();
}

该方案通过data的release-acquire同步保证数据可见性,仅在必要时发送通知,完全符合C++标准且无死锁风险。

内容的提问来源于stack exchange,提问作者Jordan Woyak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 14:15:54