0

0

c++中如何使用std::call_once_c++保证函数只执行一次的方法【汇总】

穿越時空

穿越時空

发布时间:2026-01-15 16:36:59

|

832人浏览过

|

来源于php中文网

原创

std::call_once是C++11提供的线程安全单次执行机制,确保函数在多线程下有且仅执行一次;它需配合static std::once_flag使用,避免竞态、死锁和手动同步缺陷。

c++中如何使用std::call_once_c++保证函数只执行一次的方法【汇总】

直接说结论:std::call_once 是 C++11 引入的线程安全“单次执行”机制,它配合 std::once_flag 使用,能确保某个函数在多线程环境下**有且仅被执行一次**,比手写双重检查锁更可靠、更简洁。

为什么不能用普通全局 bool + if 判断

看似简单:定义 static bool initialized = false;,然后 if (!initialized) { init(); initialized = true; } —— 但这在多线程下是竞态条件。两个线程可能同时通过 if 检查,都进入初始化块,导致 init() 执行两次。即使加了 std::mutex,也容易漏锁、死锁或性能开销大。

std::call_once 的基本用法和参数要求

std::call_once 是一个函数模板,签名是:template void call_once(std::once_flag& flag, Callable&& f, Args&&... args);

关键点:

Open Voice OS
Open Voice OS

OpenVoiceOS是一个社区驱动的开源语音AI平台

下载

立即学习C++免费学习笔记(深入)”;

  • flag 必须是 static std::once_flag 或具有静态存储期的对象(不能是局部自动变量,否则每次调用都新建 flag,失去“一次”语义)
  • f 可以是函数指针、lambda、functor,但必须可调用且无异常抛出(或你已确保它不抛异常;否则程序会调用 std::terminate
  • 所有参数(args...)会在首次调用时完美转发给 f,支持移动语义
static std::once_flag init_flag;
void init_resource() {
    // 耗时/不可重入的初始化逻辑
}
// 多线程中任意位置调用:
std::call_once(init_flag, init_resource);

// 带参数的 lambda 示例:
static std::once_flag config_flag;
std::string config_path = "/etc/app.conf";
std::call_once(config_flag, [](const std::string& p) {
    load_config(p);
}, config_path);

常见错误和兼容性注意点

实际使用中这几个坑最常被踩:

  • std::once_flag **不能复制、不能移动**,也不能用 = default 初始化(它是 constexpr 默认构造的,但只允许默认构造)—— 错误写法:std::once_flag flag = {};auto flag = std::once_flag{};
  • MSVC 在较老版本(如 VS2013)中对 std::call_once 的异常处理支持不完整;GCC/Clang 从 4.8+、MSVC 从 2015 Update 3 起行为一致
  • 不要在 std::call_once 的 callable 中再调用另一个 std::call_once 并传入同一个 std::once_flag,会导致未定义行为
  • 如果初始化函数可能抛异常,std::call_once 会认为“本次尝试失败”,后续调用仍会重试 —— 但标准规定:**只有第一次抛异常的调用会触发重试;之后若再次调用,仍会尝试执行,直到成功或再次抛异常**。这不是“失败后跳过”,而是“失败不标记完成”

真正要注意的是:flag 的生命周期和作用域。把它声明成函数内 static 最常用也最安全;若放在类里,必须是 static 成员,且确保该类不会被频繁构造析构。一旦 flag 被销毁,再用它调用 std::call_once 就是未定义行为。

相关专题

更多
if什么意思
if什么意思

if的意思是“如果”的条件。它是一个用于引导条件语句的关键词,用于根据特定条件的真假情况来执行不同的代码块。本专题提供if什么意思的相关文章,供大家免费阅读。

738

2023.08.22

javascriptvoid(o)怎么解决
javascriptvoid(o)怎么解决

javascriptvoid(o)的解决办法:1、检查语法错误;2、确保正确的执行环境;3、检查其他代码的冲突;4、使用事件委托;5、使用其他绑定方式;6、检查外部资源等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

175

2023.11.23

java中void的含义
java中void的含义

本专题整合了Java中void的相关内容,阅读专题下面的文章了解更多详细内容。

97

2025.11.27

lambda表达式
lambda表达式

Lambda表达式是一种匿名函数的简洁表示方式,它可以在需要函数作为参数的地方使用,并提供了一种更简洁、更灵活的编码方式,其语法为“lambda 参数列表: 表达式”,参数列表是函数的参数,可以包含一个或多个参数,用逗号分隔,表达式是函数的执行体,用于定义函数的具体操作。本专题为大家提供lambda表达式相关的文章、下载、课程内容,供大家免费下载体验。

204

2023.09.15

python lambda函数
python lambda函数

本专题整合了python lambda函数用法详解,阅读专题下面的文章了解更多详细内容。

190

2025.11.08

Python lambda详解
Python lambda详解

本专题整合了Python lambda函数相关教程,阅读下面的文章了解更多详细内容。

47

2026.01.05

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

480

2023.08.10

Python 多线程与异步编程实战
Python 多线程与异步编程实战

本专题系统讲解 Python 多线程与异步编程的核心概念与实战技巧,包括 threading 模块基础、线程同步机制、GIL 原理、asyncio 异步任务管理、协程与事件循环、任务调度与异常处理。通过实战示例,帮助学习者掌握 如何构建高性能、多任务并发的 Python 应用。

143

2025.12.24

Golang gRPC 服务开发与Protobuf实战
Golang gRPC 服务开发与Protobuf实战

本专题系统讲解 Golang 在 gRPC 服务开发中的完整实践,涵盖 Protobuf 定义与代码生成、gRPC 服务端与客户端实现、流式 RPC(Unary/Server/Client/Bidirectional)、错误处理、拦截器、中间件以及与 HTTP/REST 的对接方案。通过实际案例,帮助学习者掌握 使用 Go 构建高性能、强类型、可扩展的 RPC 服务体系,适用于微服务与内部系统通信场景。

8

2026.01.15

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Java 教程
Java 教程

共578课时 | 46.2万人学习

国外Web开发全栈课程全集
国外Web开发全栈课程全集

共12课时 | 1.0万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号