从C++标准视角:后申请自旋锁的线程先获锁且时间戳更晚是否合法?
问题分析:C++自旋锁场景下的输出合法性
首先看给出的C++自旋锁代码:
#include <iostream> #include <atomic> #include <thread> struct SpinLock{ std::atomic<bool> state; void lock(){ bool expected = false; while(!state.compare_exchange_strong(expected,true,std::memory_order::acquire,std::memory_order::relaxed)){ expected = false; } } void unlock(){ state.store(false,std::memory_order::release); } }; int main(){ auto spin_lock = SpinLock{false}; int i = 0; std::thread t1([&](){ std::this_thread::sleep_for(std::chrono::seconds(1)); spin_lock.lock(); auto time_stamp = std::time(nullptr); std::cout<<"t1 " <<time_stamp <<" "<< i<<"\n"; // #1 spin_lock.unlock(); }); std::thread t2([&](){ spin_lock.lock(); i = 1; auto time_stamp = std::time(nullptr); std::cout<<"t2 " <<time_stamp<<" "<< i<<"\n"; // #2 spin_lock.unlock(); }); t1.join(); t2.join(); }
用户提出的疑问是:是否存在符合C++标准的情况,使得输出为:
t1 1729172229 0
t2 1729172228 1
结论:该输出完全符合C++标准
原因如下:
- C++内存模型不绑定真实物理时间:标准只定义了操作间的同步关系、修改顺序,不要求代码的执行顺序和物理时间严格对应。
- 锁的获取顺序与时间戳读取顺序无关:
当#1读取到i=0时,说明t1的CAS操作(lock中的compare_exchange_strong)在原子变量state的修改顺序中早于t2的CAS——也就是t1先抢占到了锁。此时t2还在自旋等待锁,它对i=1的写入还没执行,所以t1读取到i的初始值0是合理的。
但t2的std::time(nullptr)调用完全可以在t1睡眠的1秒内就执行:t2启动后立刻进入lock循环,在自旋等待锁的过程中,可能早于t1醒来的时间就执行了时间戳读取(只是因为没拿到锁,后续的打印操作被延迟到t1解锁后才完成)。这就导致t2的时间戳数值更小(更早),但打印顺序在t1之后。 - 同步关系的约束范围:自旋锁的
acquire/release语义只约束锁保护的临界区内的操作(比如t1的i读取、t2的i写入),但std::time的调用不在临界区的依赖链中,不受锁的同步关系约束,因此它的执行时间可以和锁的获取顺序脱节。
内容的提问来源于stack exchange,提问作者xmh0511
相关产品推荐
相关产品推荐

