c++中如何使用std::atomic_flag_c++最简单的自旋锁实现【详解】
技术百科
尼克
发布时间:2026-01-27
浏览: 次 std::atomic_flag 是最轻量的无锁原子布尔类型,专为实现自旋锁设计,仅支持 test_and_set() 和 clear(),强制 lock-free 且初始化必须用 ATOMIC_FLAG_INIT 或 {}。
std::atomic_flag 是 C++ 中最轻量的原子布尔类型,它不提供 load() 或 store(),只支持 test_and_set() 和 clear() —— 这恰恰让它成为实现**无锁自旋锁**的理想起点。
为什么不用 std::atomic 做自旋锁?
看似更直观,但 std::atomic 的 exchange(true, std::memory_order_acquire) 在某些平台(如 ARM)可能生成较重的指令;而 std::atomic_flag 被标准强制要求是“无锁的”(lock-free),且 test_and_set() 默认使用 std::memory_order_seq_cst,语义更贴近互斥锁的 acquire/release 行为。
-
std::atomic_flag初始化必须用ATOMIC_FLAG_INIT(C++17 起可直接用{}值初始化) - 它不支持拷贝、赋值,天然防误用
- 没有
operator bool(),必须显式调用test_and_set()或clear()
最简自旋锁:spin_lock 类封装
核心逻辑就两行:循环调用 test_and_set() 直到返回 false(说明之前是未设置状态,抢锁成功);退出临界区时调用 clear()。
struct spin_lock {
std::atomic_flag flag = ATOMIC_FLAG_INIT;
void lock() {
while (flag.test_and_set(std::me
mory_order_acquire)) {
// 可选:__builtin_ia32_pause() 或 std::this_thread::yield()
}
}
void unlock() {
flag.clear(std::memory_order_release);
}
};
- 用
std::memory_order_acquire保证 lock 后的读写不被重排到锁获取前 - 用
std::memory_order_release保证 unlock 前的读写不被重排到锁释放后 - 若长期争用,空转消耗 CPU,建议在循环体内加
std::this_thread::yield()或 x86 的_mm_pause()
常见误用与陷阱
看似简单,但几个细节一错就导致死锁或数据竞争:
- 忘记初始化:写成
std::atomic_flag flag;是未定义行为 —— 必须用= ATOMIC_FLAG_INIT或{}(C++17) - unlock 时用了错误的 memory order:
flag.clear(std::memory_order_relaxed)会破坏同步语义,其他线程可能看不到临界区内的修改 - 构造函数里没初始化 flag,或多次调用
clear()而未配对lock(),会导致锁状态混乱 - 把
spin_lock放在栈上并跨线程传递(比如 move 到 lambda)—— 它不可移动、不可拷贝,运行时报错或静默 UB
和 std::mutex 对比:什么时候该用它?
自旋锁只适合「临界区极短 + 争用极少」的场景,比如保护一个计数器更新、或无锁结构中的某个标志位。
- 优势:无系统调用开销,上下文切换少,在低延迟关键路径中更快
- 劣势:持有锁时持续占用 CPU,高争用下吞吐反而更差;无法被操作系统调度器挂起,不能用于可能阻塞的操作(如 IO、sleep)
- 不要试图用它替代
std::mutex写业务逻辑 —— 几乎总是错的
真正需要它的场合,往往已经处于无锁编程的深水区,此时你大概率已在手写 std::atomic 状态机,而不是靠 std::atomic_flag 打天下。
# 操作系统
# 放在
# 几个
# 它不
# 用它
# 不被
# 什么时候
# 已在
# 循环
# c++
# int
# 构造函数
# 为什么
# 线程
# 栈
# 死锁
# 无锁
# operator
# 封装
# Lambda
# bool
# 布尔
# 有锁
# 最轻
# 天下
# 布尔类型
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- c++如何判断文件是否存在_c++ filesys
- c++如何连接Redis c++ hiredis库
- 如何使用Golang构建简易投票统计功能_Gola
- Win11怎么设置虚拟桌面 Win11新建多桌面切
- Windows怎样关闭锁屏广告_Windows关闭
- 如何在Golang中写入XML文件_生成符合规范的
- Win10怎么卸载爱奇艺_Win10彻底卸载爱奇艺
- 如何更改Windows资源管理器的默认启动位置?(
- 如何自定义Windows终端的默认配置文件?(Po
- LINUX的SELinux是什么_详解LINUX强
- 如何使用Golang实现函数指针_函数变量与回调示
- Windows电脑如何截屏?(四种快捷方法)
- Ajax提交表单PHP怎么接收_处理Ajax发送的
- Mac怎么设置鼠标滚动速度_Mac鼠标设置详细参数
- Windows10系统怎么查看防火墙状态_Win1
- MySQL 中使用 IF 和 CASE 实现查询字
- PHP怎么接收URL中的锚点参数_获取#后面参数值
- c++ unordered_map怎么用 c++哈
- VSC怎样在VSC中调试PHPAPI_接口调试技巧
- Windows10系统怎么查看显卡驱动_Win10
- 如何从 Go 的 map[string]inter
- php打包exe如何加密代码_防反编译保护方法【技
- Win11怎么激活Windows10_Win11激
- Win11怎么更改电脑密码_Windows 11修
- Win11怎么设置任务栏透明_Windows11使
- PHP的Workerman对架构扩展有啥帮助_应用
- Win11无法识别耳机怎么办_解决Win11插耳机
- Windows10电脑怎么设置虚拟光驱_Win10
- Win11任务栏怎么固定应用 Win11将软件图标
- XAMPP 启动失败(Apache 突然停止)的终
- 如何使用Golang实现云原生应用弹性伸缩_自动应
- Win10如何备份驱动程序_Win10驱动备份步骤
- Win10如何关闭安全中心所有通知 Win10禁用
- Win11怎样安装微信开发者工具_Win11安装开
- Windows Defender扫描失败怎么办_安
- Win11怎么设置快速访问主页_Windows11
- Win11怎么查看wifi信号强度_检测Windo
- Win11 explorer.exe频繁崩溃_修复
- php8.4如何配置ssl证书_php8.4htt
- 如何在Golang中使用container/hea
- 如何在Golang中编写端到端测试_Golang
- 微信JSAPI支付回调PHP怎么接收_处理JSAP
- Windows怎样关闭桌面弹窗广告_Windows
- php订单日志怎么记录物流_php记录订单物流变更
- Win11怎么更改任务栏位置_修改注册表将Win1
- PythonGIL机制理解_多线程限制解析【教程】
- c# 服务器GC和工作站GC的区别和设置
- Go 中 := 短变量声明的类型推导机制详解
- c++中的CRTP是什么 c++奇异递归模板模式【
- mac怎么安装pip_MAC Python pip


QQ客服