c++11总结21——atomic_flag

本文详细介绍了C++中的std::atomic_flag,包括其无锁特性、成员函数如clear、test_and_set等。通过示例展示了如何使用std::atomic_flag实现自旋互斥,强调了test_and_set的原子性及其在内存序中的作用。同时提到了C++20新增的wait、notify_one和notify_all功能,用于线程间的同步。

1. 头文件

#include <atomic>

2. 功能

是原子布尔类型,它保证是无锁的(lock-free),且不提供加载或存储操作。

3. 成员函数

构造函数
  • atomic_flag() noexcept = default;
  • atomic_flag (const atomic_flag&T) = delete;
  • operator= is deleted (non-copyable/moveable)

说明:拷贝构造函数和赋值函数被禁用,不允许从一个atomic_flag对象构造另一个对象

clear原子的设置标志为false
test_and_set原子的设置标志为true,并获得其先前值
test(c++20)原子的返回标志的值
wait(c++20)阻塞线程直至被提醒且原子值更改
notify_one(c++20)提醒至少一个在原子对象上的等待中阻塞的线程
notify_all(c++20)提醒所有在原子对象上的等待中阻塞的线程

4. ATOMIC_FLAG_INIT宏

如果某个std::atomic_flag 对象使用该宏初始化,那么可以保证该 std::atomic_flag 对象在创建时处于 clear 状态,即标志为false。

5. std::atomic_flag::test_and_set

5.1 原型

bool test_and_set (memory_order sync = memory_order_seq_cst) volatile noexcept;
bool test_and_set (memory_order sync = memory_order_seq_cst) noexcept;

5.2 作用

test_and_set() 函数检查 std::atomic_flag 标志,如果 std::atomic_flag 之前没有被设置过,则设置 std::atomic_flag 的标志,并返回先前该 std::atomic_flag 对象是否被设置过,如果之前 std::atomic_flag 对象已被设置,则返回 true,否则返回 false。

test-and-set 操作是原子的(因此 test-and-set 是原子 read-modify-write (RMW)操作)。

test_and_set 可以指定 Memory Order(后续作补充):

Memory Order 值Memory Order 类型
memory_order_relaxedRelaxed
memory_order_consumeConsume
memory_order_acquireAcquire
memory_order_releaseRelease
memory_order_acq_relAcquire/Release
memory_order_seq_cstSequentially consistent

6. std::atomic_flag::clear()

6.1 原型

void clear (memory_order sync = memory_order_seq_cst) volatile noexcept;
void clear (memory_order sync = memory_order_seq_cst) noexcept;

6.2 作用

清除 std::atomic_flag 标志使得下一次调用 std::atomic_flag::test_and_set 返回 false。

7. 自旋互斥

#include <thread>
#include <vector>
#include <iostream>
#include <atomic>
 
std::atomic_flag lock = ATOMIC_FLAG_INIT;
 
void myFunc(int n)
{
    for (int cnt = 0; cnt < 100; ++cnt) {
        while (lock.test_and_set(std::memory_order_acquire))  // 获得锁
             ; // 自旋
        std::cout << "Output from thread " << n << '\n';
        lock.clear(std::memory_order_release);               // 释放锁
    }
}
 
int main()
{
    std::vector<std::thread> v;
    for (int n = 0; n < 10; ++n) {
        v.emplace_back(myFunc, n);
    }
    for (auto& t : v) {
        t.join();
    }
}

部分参考:

https://www.cnblogs.com/haippy/p/3252056.html

https://www.apiref.com/cpp-zh/cpp/atomic/atomic_flag.html

### C++ 中 `std::atomic_flag` 的初始化机制 #### 默认构造函数未完全初始化的原因 在 C++ 标准库中,`std::atomic_flag` 是一种轻量级的原子标志类,其设计目标是为了支持高效的线程间同步[^3]。然而,默认情况下,如果没有显式地使用 `ATOMIC_FLAG_INIT` 进行初始化,则新创建的 `std::atomic_flag` 对象的状态将是未定义的(unspecified)[^2]。这是因为编译器无法保证在动态分配内存或其他复杂场景下自动完成正确的初始状态设定。 这种行为的根本原因在于性能考虑——为了保持最小开销并允许更底层控制,C++ 并不强制执行隐式的零化或特定值填充操作于所有类型的对象之上;特别是对于像 `std::atomic_flag` 这样旨在提供高效并发原语的数据结构而言更是如此。因此,在某些平台上可能依赖硬件特性来决定实际表现形式,而这些平台上的默认构造未必能确保一致的结果[^4]。 #### 使用 atomic_init 正确初始化的方式 为了避免上述不确定性带来的潜在问题,应该采用专门提供的宏 `ATOMIC_FLAG_INIT` 来手动初始化每一个实例化的 `std::atomic_flag` 变量: ```cpp #include <atomic> // 利用 ATOMIC_FLAG_INIT 宏来进行初始化 std::atomic_flag flag = ATOMIC_FLAG_INIT; ``` 此外,自 C++17 开始还引入了一个新的工具函数模板 `std::atomic_init` ,它能够帮助我们更加灵活安全地处理这类情况: ```cpp #include <atomic> #include <iostream> int main(){ std::atomic_flag f; // 使用 atomic_init 函数进行初始化 std::atomic_init(&f, false); return 0; } ``` 这里需要注意的是尽管两者都可以达到相同目的即把标志位设为已清除(false),但推荐优先选用后者因为它的语法更为直观易懂而且适用范围广一些。 ### 总结 综上所述,由于效率考量以及跨平台兼容性的需求,C++并未让默认构造函数承担起全面初始化的责任而是交由开发者自行解决这个问题。通过运用恰当的方法比如利用 `ATOMIC_FLAG_INIT` 或者调用 `std::atomic_init()` 就可以有效规避掉那些源于未知初态所引发的风险。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值