C++ 中的 std::atomic_flag_test_and_set, std::atomic_flag_test_and_set_explicit 函数

2025年5月14日 | 阅读 4 分钟

C++ 中的 std::atomic_flag_test_and_set 和 std::atomic_flag_test_and_set_explicit 函数是 <atomic> 库的一部分,在实现无锁、线程安全的操作中至关重要。这些函数作用于 std::atomic_flag,它是一种简单的原子类型,专门用于布尔标志,具有两种可能的状态:设置和清除。这对于实现基本的自旋锁非常有用。

std::atomic_flag_test_and_set

目的:此函数用于检查标志当前是否已设置 (true),如果未设置,则设置标志。所有这些操作都是原子性的,这意味着一旦一个线程修改了标志,其他线程就无法同时修改。这对于确保函数操作是线程安全的至关重要。

用法: flag.test_and_set()

返回值:它返回标志的先前值(true 或 false)。如果标志已设置,函数将返回 true。如果标志未设置,它将把标志设置为 true 并返回 false。

示例用例: std::atomic_flag_test_and_set 的典型用例是在自旋锁中。线程可以循环重复调用 test_and_set,直到函数返回 false,表示它成功设置了标志并“锁定了”资源。

示例代码

输出

 
In critical section
In critical section 

说明

在此示例中,test_and_set 用于获取锁。如果标志已设置,它会一直循环(自旋),直到另一个线程清除标志。当临界区完成后,lock_flag.clear() 释放锁,允许其他线程进入。

std::atomic_flag_test_and_set_explicit

目的: std::atomic_flag_test_and_set_explicit 是 test_and_set 的一个更高级版本,允许程序员指定内存顺序。内存顺序会影响不同线程之间如何感知标志上的操作,这可能会影响多线程程序的性能和正确性。

用法: flag.test_and_set_explicit(order)

参数

  • 顺序:这是操作的内存顺序,可以是以下几种选项之一
  • std::memory_order_relaxed:无同步或排序约束。
  • std::memory_order_acquire:确保当前线程中在 acquire 之后的操作不会被移到 acquire 之前。
  • std::memory_order_release:确保当前线程中在 release 之前的所有操作都不会被移到 release 之后。
  • std::memory_order_acq_rel:同时具有 acquire 和 release 语义。
  • std::memory_order_seq_cst:确保线程之间的顺序一致性。

返回值:与 test_and_set 类似,它返回标志的先前状态(如果已设置,则为 true;如果未设置,则为 false)。

示例代码

std::atomic_flag_test_and_set 和 std::atomic_flag_test_and_set_explicit 函数在 C++ 中的主要区别

这些 函数之间的一些主要区别如下

特性atomic_flag_test_and_setatomic_flag_test_and_set_explicit
目的原子地检查标志是否已设置,如果未设置,则将其设置为 true。主要用于简单的同步任务,如基本自旋锁或互斥锁实现。原子地检查并设置标志,可以选择指定内存顺序,使其适用于更受控和复杂的多线程任务。
内存排序使用默认内存顺序,通常是 std::memory_order_seq_cst(顺序一致性),确保最严格的顺序以使操作按顺序对所有线程可见。允许指定内存顺序,为细粒度性能调整提供对操作可见性和重新排序的更多控制。
返回值返回标志的先前状态(如果已设置,则为 true;如果未设置且现在已设置,则为 false)。返回标志的先前状态(如果已设置,则为 true;如果未设置且刚刚设置,则为 false),提供具有更多内存效果控制的原子测试并设置。
常见用例非常适合基本的同步需求,例如实现简单的自旋锁,其中一个线程必须“锁定”资源,而其他线程等待。例如,当一个线程在临界区时,其他线程自旋直到锁被释放。适用于更高级的场景,其中显式内存排序对于性能至关重要,例如高性能并发数据结构或需要精确管理内存可见性的低级同步机制。
语法flag.test_and_set()flag.test_and_set_explicit(order)
内存顺序选项隐式,通常是 std::memory_order_seq_cst,它提供强大的顺序一致性保证,但在高频访问模式下可能会产生性能开销。接受特定的内存排序参数,例如:
  • std::memory_order_relaxed - 最小同步,仅原子性。
  • std::memory_order_acquire - 确保在当前线程中,任何读取或写入操作都不会在该操作之前重新排序。
  • std::memory_order_release - 确保在当前线程中,任何读取或写入操作都不会在该操作之后重新排序。
  • std::memory_order_acq_rel - 结合了 acquire 和 release 语义。
  • std::memory_order_seq_cst - 确保所有操作在所有线程中保持顺序一致性。
性能影响在重新排序和可见性方面灵活性较低,在高性能应用程序中可能会引入开销,而这些应用程序可能只需要较宽松的排序。它允许通过选择最适合用例的内存顺序进行优化。例如,使用 memory_order_relaxed 可以通过避免不必要的排序约束来提高性能。
示例用法基本自旋锁示例:cpp
while (flag.test_and_set()) { /* 自旋 */ }
// 临界区
flag.clear();
带显式内存顺序的自旋锁:cpp
while (flag.test_and_set_explicit(std::memory_order_acquire)) { /* 自旋 */ }
// 临界区
flag.clear(std::memory_order_release);
何时使用当需要健壮的同步而不需要精细的性能控制时适用。适用于依赖标准内存顺序的通用任务。当需要特定的内存顺序要求进行性能调整时,或者在处理低级优化时,其中管理重新排序或内存可见性可以提高整体效率时推荐使用。