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

C++自定义原子锁是否存在代码重排风险,导致锁防护失效?

问题

我实现了一个用于防止多线程访问同一代码段的锁:

struct SimpleAtomicThreadLock
{
constexpr SimpleAtomicThreadLock() : bIsLocked(false) {}
std::atomic<bool> bIsLocked;

void lock()
{
    while (bIsLocked.exchange(true));
}
void unlock()
{
    bIsLocked.store(false);
}
};

并按如下方式使用该锁:

void Mesh::drawMesh()
{
    static constexpr SimpleAtomicThreadLock lock;

    lock.lock();
        /* BOTH MESH AND MATERIAL NEED TO BE MADE SHADER ACCESS READY */
        if (!mesh->isDeviceLocal() && !mesh->bIsBeingMadeDeviceLocal)
        {
            Engine::makeMeshDeviceLocal(mesh);
        }
        if (!this->material->isReadyForShaderUse() && !this->material->bIsBeingMadeReadyForShaderUse)
        {
            Engine::makeMaterialShaderAccessReady(material);
        }

        lock.unlock();
}

我正在学习C++内存模型中的原子操作相关知识,了解到编译器通常会对函数内的语句进行重排。请问在上述场景中,锁与解锁之间的代码是否可能被重排到lock()调用之前?

回答

不会,lock()和unlock()之间的临界区代码绝对不可能被重排到lock()调用之前,核心原因是原子操作带来的内存约束:

  • 原子操作的内存序规则
    bIsLocked.exchange(true)默认采用的是std::memory_order_seq_cst(顺序一致内存序),这是C++内存模型里最严格的内存序。它会生成一个全局内存屏障,强制要求:

    • 所有在这个exchange操作之前的内存访问,不能被重排到它之后;
    • 所有在这个exchange操作之后的内存访问(也就是你的临界区代码),不能被重排到它之前。
  • 编译器的优化边界
    编译器的代码重排必须严格遵守C++内存模型,不能破坏原子操作的语义。如果把临界区代码重排到lock()之前,就相当于跳过锁直接执行线程不安全的代码,这完全违反了标准规定的原子操作内存约束,编译器不会做这种优化。

  • 补充:unlock的内存保障
    顺带提一句,unlock()里的bIsLocked.store(false)默认也是std::memory_order_seq_cst,它会确保临界区的所有内存操作都完成后,才会执行解锁的存储操作,防止临界区代码被重排到unlock()之后——不过这和你的问题无关,只是额外的保障。

总结一下,std::atomic的原子操作(尤其是默认的顺序一致内存序)已经从语言层面禁止了这种危险的重排,你不用担心临界区代码跑到锁前面执行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 05:45:41