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

C++事务内存中atomic_commit的精准含义及相关技术问询

C++事务内存atomic_commit行为详解

我正在阅读cppreference上的事务内存参考文档,对其中atomic_commit的部分描述感到困惑——文档说明“若抛出异常,事务会正常提交”。我已在Godbolt中运行了示例代码,虽有大致预期,但希望得到确切的行为解释,而非个人推测。我主要使用最新版本的GCC。

示例代码:

auto f() -> std::optional<int> {
    static int i = 0;

    std::optional<int> p;
    atomic_commit {  

        //some memory ops happen here
        ++i;
        throw std::runtime_error("I decide to fail here, for some reason");
        

        p = i;
        return p;  // commit transaction
    }

    return std::nullopt;
}

int main() {
  for (int retries = 0 ; retries < 10 ; ++retries) {
    try {
        auto r = f();
        if (r) {
            std::cout << r.value() << std::endl;
        } else {
            std::cout << "nope" << std::endl;
        }
    } catch (...) {
        //handle the exception and maybe retry.
    }
  }
}

问题解答

1. 已修改的数据会发生什么?是否会回滚?

根据cppreference对atomic_commit的定义:若事务块内抛出异常,事务仍会正常提交。也就是说,示例中++i的修改会被永久保留,不会回滚。异常会在事务提交完成后,再进行栈展开。这和atomic_cancel的行为完全相反——atomic_cancel会在异常发生时回滚所有修改,再展开栈。

2. 若两个线程尝试写入同一地址,第一个线程操作失败,第二个线程会怎样?数据是否会损坏?

首先需要明确:atomic_commit的事务块是原子执行的,要么所有修改都提交,要么都不提交(但异常情况除外,如问题1所述)。如果第一个线程的事务因为冲突(比如和第二个线程同时修改同一地址)而失败(不是抛出异常,而是事务本身的冲突中止),那么第一个线程的所有修改都会被回滚,不会对共享数据造成影响。此时第二个线程的事务可以正常执行,数据不会损坏——事务内存的核心保证就是避免数据竞争和损坏,即使出现冲突,也会通过重试或回滚保证一致性。

注意:如果第一个线程是因为抛出异常导致事务提交,那它的修改已经生效,第二个线程的事务会看到更新后的数据,后续操作基于新值执行,同样不会出现数据损坏。

3. 若无异常抛出,两个线程写入同一地址时,具体会发生什么?

当两个线程的atomic_commit事务同时修改同一地址时,会触发事务冲突。此时GCC的事务内存实现会自动处理:其中一个线程的事务会成功提交,另一个线程的事务会被中止。被中止的事务会回滚所有修改,然后需要调用者自行决定是否重试(比如示例main函数中的循环重试逻辑)。整个过程是原子性的,不会出现部分修改生效的情况,数据始终保持一致。

关于atomic_cancel的补充

atomic_cancel的行为相对直观:无论是否抛出异常,只要事务块没有正常完成(比如抛出异常或被中止),所有修改都会回滚到事务开始时的状态,之后再进行栈展开。不过目前GCC尚未完全实现该特性,实际使用中可能存在限制。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 19:05:39