多线程场景下数据可见性问题的技术咨询
多线程场景下数据可见性问题的技术咨询
嘿,咱们直接聚焦你最关心的数据可见性核心问题,先把两个案例的关键逻辑理清楚,再给你明确的结论。
首先回顾你的两个代码场景:
1. 带任务队列的Sample场景
class Sample { int a = 0; public: void Run() { // main thread.Assuming this is chromium task runner. auto currentRunner = GetCurrentDefault(); somePooledRunner->PostTask( [currentRunner, this]() { a = TimeConsumingOperation(); // post back to main thread. currentRunner->PostTask([this]() { OnComplete1(a); OnComplete2(); }); }); } void OnComplete1(int aParameter) { Process(aParameter); } void OnComplete2() { Process(this->a); // Is there a data visibility issue here that I'm overlooking but is being handled correctly by the library I'm using? } };
你提到这类依赖成熟异步库(比如Chromium Task Runner、SynchronizationContext)的场景,其实库的实现已经通过锁或无锁队列的同步逻辑,隐式建立了happens-before关系:工作线程对a的写入,会在主线程执行后续任务时被保证可见。这也是你实际用起来没问题的原因——库帮你处理了底层的内存同步问题。
2. 简化后的SimpleSample场景
class SimpleSample { int a = 0; void Run() { auto t = std::thread([this]() { a = TimeConsumingOperation(); }); t.detach(); // To simplify the problem, no synchronization mechanism is introduced, and only sleep is performed. Sleep(enoughTime); // Assume that the sleep time is sufficient for TimeConsumingOperation to complete and write a in worker thread. // Is there a possibility that I cannot obtain the calculation result of a due to data visibility? // printf(a); Process(a); } };
你的核心疑问:即使主线程Sleep足够时间,确保工作线程已经写完a,有没有可能主线程还是读不到更新后的a?
明确结论:从C++标准角度,存在数据可见性风险,属于未定义行为
原因在于C++内存模型的核心规则:两个线程对同一非原子变量的读写操作,如果没有建立明确的happens-before关系,那么行为是未定义的。
在SimpleSample中:
- 工作线程对
a的写入操作,和主线程对a的读取操作之间,没有任何符合标准的同步机制(比如std::join、互斥锁、条件变量、原子操作等)。 Sleep()只是让主线程暂停执行一段时间,但它不提供任何内存同步语义——既不会插入内存屏障,也不会强制CPU刷新缓存,更不会在编译器层面阻止指令重排或变量缓存优化。
虽然在大多数实际运行环境中,你可能大概率能读到正确的a值,但这完全依赖于具体的编译器实现、CPU架构和操作系统行为,并不是C++标准所保证的。严格来说,编译器完全有权限:
- 把主线程中
Process(a)的a读取优化为直接使用初始值0(比如把a缓存到寄存器,根本不重新从内存读取); - 重排指令,让
Process(a)的读取操作在Sleep()之前执行(虽然逻辑上不合理,但标准允许这种优化,因为没有同步约束)。
这些情况都会导致你无法获取到工作线程计算后的a值,也就是你担心的数据可见性问题。
而你第一个Sample中依赖的异步库(比如Chromium Task Runner),其PostTask接口的实现会在任务提交和执行的前后,通过锁、原子操作或内存屏障等方式,隐式建立happens-before关系,从而保证了数据可见性——这就是库帮你处理了底层的内存同步问题。
内容来源于stack exchange
相关产品推荐
相关产品推荐

