【C/C++】线程安全初始化:std::call_once详解
std::call_once
使用详解
std::call_once
是 C++11 标准库中提供的一个线程安全的一次性调用机制,位于 <mutex>
头文件中。它确保某个可调用对象只被执行一次,即使多个线程同时尝试调用它。
基本用法
#include <mutex>
#include <thread>std::once_flag flag; // 全局或静态的once_flagvoid initialize() {// 初始化代码(只执行一次)
}void thread_function() {std::call_once(flag, initialize);
}int main() {std::thread t1(thread_function);std::thread t2(thread_function);t1.join();t2.join();
}
核心组件
-
std::once_flag
- 轻量级对象,用于标记函数是否已被调用
- 必须是非局部的(全局/静态/类静态成员)
- 不可复制、不可移动、不可赋值
-
std::call_once
- 函数模板:
template <class Callable, class... Args> void call_once(once_flag& flag, Callable&& func, Args&&... args);
- 保证
func
只执行一次 - 线程安全:其他线程会阻塞直到初始化完成
- 函数模板:
关键特性
-
线程安全保证:
- 只有一个线程会执行函数
- 其他线程会阻塞直到函数执行完成
- 执行完成后,所有线程都能看到初始化结果
-
异常处理:
- 如果函数抛出异常,异常会传播给调用者
once_flag
不会被标记为完成,其他线程会重试执行- 需要确保函数在重试时能成功
-
性能特点:
- 初始化完成后只有原子检查的开销
- 初始化期间其他线程会阻塞
- 比双重检查锁定更简单安全
使用场景
1. 延迟初始化(Lazy Initialization)
class ExpensiveResource {
public:static ExpensiveResource& getInstance() {static std::once_flag initFlag;std::call_once(initFlag, [] {instance.reset(new ExpensiveResource());});return *instance;}private:static std::unique_ptr<ExpensiveResource> instance;ExpensiveResource() { /* 耗时的初始化 */ }
};
延迟初始化,使用static Singleton instance; return instance;
形式更优
2. 线程安全的单例模式
class Logger {
public:static Logger& getInstance() {static std::once_flag flag;std::call_once(flag, [] {instance.reset(new Logger());});return *instance;}void log(const std::string& message) {std::lock_guard<std::mutex> lock(mutex);// 线程安全的日志记录}private:static std::unique_ptr<Logger> instance;std::mutex mutex;Logger() = default;
};
3. 初始化共享资源
class DatabaseConnection {
public:static DatabaseConnection& getConnection() {static std::once_flag initFlag;std::call_once(initFlag, &DatabaseConnection::init, this);return *this;}private:void init() {// 建立数据库连接}
};
4. 一次性配置加载
class Config {
public:static const Config& load() {static std::once_flag flag;static Config instance;std::call_once(flag, [] {instance.loadFromFile("config.json");});return instance;}private:void loadFromFile(const std::string& path) {// 从文件加载配置}
};
5. 插件系统初始化
class PluginManager {
public:void initialize() {std::call_once(initFlag, [this] {loadPlugins();registerHooks();initEventSystem();});}private:std::once_flag initFlag;
};
与局部静态变量的对比
特性 | std::call_once | 局部静态变量 (C++11+) |
---|---|---|
语法复杂度 | 较复杂 | 简单 |
控制粒度 | 精细控制 | 整个函数 |
多位置调用 | 支持多个位置调用相同初始化 | 只能在一个位置初始化 |
成员函数初始化 | 可直接用于成员函数 | 只能用于静态成员或全局函数 |
初始化参数 | 可传递参数 | 无参数 |
多次初始化不同函数 | 支持 | 不支持 |
性能 | 初始化后开销小 | 相同 |
异常处理 | 显式处理,可重试 | 编译器处理 |
最佳实践
- 只用于真正的"一次性"操作:不要用于可能多次初始化的场景
- 避免在性能关键路径使用:初始化期间会阻塞其他线程
- 确保函数幂等性:即使多次调用也不会产生副作用(考虑异常情况)
- 配合智能指针管理资源:避免资源泄漏
- 谨慎处理异常:确保在重试时能成功完成初始化
- 避免递归调用:不要在call_once的函数内再次调用call_once
错误用法示例
// 错误1:局部once_flag(每次调用都会新建)
void unsafe_init() {std::once_flag flag; // 错误!每次调用都是新的flagstd::call_once(flag, []{ /* ... */ });
}// 错误2:尝试多次初始化
void double_init() {static std::once_flag flag;std::call_once(flag, []{ /* 初始化A */ });std::call_once(flag, []{ /* 初始化B */ }); // 永远不会执行
}// 错误3:异常处理不当
void risky_init() {static std::once_flag flag;std::call_once(flag, []{if (/* 条件 */) {throw std::runtime_error("Oops");}}); // 抛出异常后flag未标记,其他线程会重试
}
总结
std::call_once
是 C++ 中实现线程安全一次性初始化的强大工具,特别适用于:
- 延迟初始化昂贵资源
- 实现线程安全的单例
- 加载配置或资源
- 初始化共享状态
相比于传统的双重检查锁定模式,std::call_once
提供了更简洁、更安全的替代方案,避免了复杂的同步逻辑和潜在的内存排序问题。在 C++11 及以上环境中,它是实现线程安全初始化的重要工具。
相关文章:
【C/C++】线程安全初始化:std::call_once详解
std::call_once 使用详解 std::call_once 是 C11 标准库中提供的一个线程安全的一次性调用机制,位于 <mutex> 头文件中。它确保某个可调用对象只被执行一次,即使多个线程同时尝试调用它。 基本用法 #include <mutex> #include <thread…...

技术分享 | Oracle SQL优化案例一则
本文为墨天轮数据库管理服务团队第70期技术分享,内容原创,作者为技术顾问马奕璇,如需转载请联系小墨(VX:modb666)并注明来源。 一、问题概述 开发人员反映有条跑批语句在测试环境执行了很久都没结束&…...
什么是RFID电子标签
RFID 电子标签是用于物品标识、具有信息存储机制、能接收读写器的电磁场调制信号并返回响应信号的数据载体,通常被称为电子标签,也可称作射频卡、射频标签、射频卷标等,是与读写器一起构成 RFID 系统的硬件主体。 RFID 系统基本组成包括RFID电子标签、读写器、射频天线、应用…...

华为手机用的时间长了,提示手机电池性能下降,需要去换电池吗?平时要怎么用能让电池寿命长久一些?
华为手机提示电池性能下降时,是否需要更换电池以及如何延长电池寿命,取决于电池老化程度和使用习惯。以下是具体分析和建议: 一、是否需要更换电池? 电池健康度低于80% 如果手机提示“电池性能下降”,通常意味着电池…...

BERT***
1.预训练(Pre-training) 是深度学习中的一种训练策略,指在大规模无标注数据上预先训练模型,使其学习通用的特征表示,再通过微调(Fine-tuning) 适配到具体任务 2.sentence-lev…...

超级对话2:大跨界且大综合的学问融智学应用场景述评(不同第三方的回应)之二
摘要:《人机协同文明升维行动框架》提出以HIAICI/W公式推动认知革命,构建三大落地场景:1)低成本认知增强神经接口实现300%学习效率提升;2)全球学科活动化闪电战快速转化知识体系;3)人…...
在Linux环境里面,Python调用C#写的动态库,如何实现?
在Linux环境中,Python可以通过pythonnet(CLR的Python绑定)或subprocess调用C#动态库。以下是两种方法的示例: 方法1:使用pythonnet(推荐) 前提条件 安装Mono或.NET Core运行时安装pythonnet包…...
【Linux 基础知识系列】第三篇-Linux 基本命令
在数字化浪潮席卷全球的当下,操作系统作为计算机系统的核心组件,扮演着至关重要的角色。而 Linux,凭借其卓越的性能、高度的稳定性和出色的可定制性,在服务器、嵌入式系统、超级计算机以及个人计算机等领域大放异彩,成…...
OpenCV CUDA模块直方图计算------生成一组均匀分布的灰度级函数evenLevels()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 该函数主要用于为 直方图均衡化、CLAHE 等图像处理算法 生成一组等间距的灰度区间边界值(bins 或 levels),这…...

深度学习常见实验问题与实验技巧
深度学习常见实验问题与实验技巧 有一定的先后顺序的 还在迷茫深度学习中的改进实验应该从哪里开始改起的同学,一定要进来看看了!用自身经验给你推荐实验顺序! YOLOV8-硬塞注意力机制?这样做没创新!想知道注意力怎么…...
前端面试之Proxy与Reflect
🌟 一、Proxy 与 Reflect 的核心概念 1. Proxy:代理拦截器 Proxy 用于创建对象的代理,拦截并自定义对象的基本操作(如属性读写、函数调用等)。 核心组成: 目标对象(Targe…...
uniapp vue3 鸿蒙支持的 HTML5+接口
uniapp vue3 编译鸿蒙所支持的 HTML5接口 文档:https://www.html5plus.org/doc/zh_cn/runtime.html {"geolocation": {//获取当前设备位置信息"getCurrentPosition": function() {},//监听设备位置变化信息"watchPosition": functi…...

一张Billing项目的流程图
流程图 工作记录 2016-11-11 序号 工作 相关人员 1 修改Payment Posted的导出。 Claim List的页面加了导出。 Historical Job 加了Applied的显示和详细。 郝 识别引擎监控 Ps (iCDA LOG :剔除了160篇ASG_BLANK之后的结果): LOG_File 20161110.txt BLANK_CDA/ALL 45/10…...

理想树图书:以科技赋能教育,开启AI时代自主学习新范式
深耕教育沃土 构建全场景教辅产品矩阵 自2013年创立以来,理想树始终以教育匠心回应时代命题。在教辅行业这片竞争激烈的领域,由专业教育工作者组成的理想树图书始终秉持“知识互映”理念,经过十余年的精耕细作,精心打造了小学同步…...

【大模型02】Deepseek使用和prompt工程
文章目录 DeepSeekDeepseek 的创新MLA (低秩近似) MOE 混合专家混合精度框架总结DeepSeek-V3 与 DeepSeek R1 DeepSeek 私有化部署算例市场: autoDLVllM 使用Ollma复习 API 调用deepseek-r1Prompt 提示词工程Prompt 实战设置API Keycot 示例p…...
B端产品经理如何快速完成产品原型设计
B 端产品经理的原型设计需兼顾业务流程复杂度、功能逻辑性和操作效率,快速完成原型的核心在于结构化梳理需求、复用成熟组件、借助高效工具、聚焦核心场景。以下是具体方法和步骤: 一、明确需求优先级:先框架后细节 1. 梳理业务流程&#x…...
[Java实战]Spring Boot切面编程实现日志记录(三十六)
[Java实战]Spring Boot切面编程实现日志记录(三十六) 一、AOP日志记录核心原理 1.1 AOP技术体系 Spring AOP基于代理模式实现,关键组件: JoinPoint:程序执行点(方法调用/异常抛出)Pointcut:切点表达式(定义拦截规则)Advice:增强逻辑(前置/环绕/异常通知)Weaving:…...
Apache POI生成的pptx在office中打不开 兼容问题 wps中可以打卡问题 POI显示兼容问题
项目场景: 在java服务中使用了apache.poi后生成的pptx在wps中打开是没有问题,但在office中打开显示如下XXX内容问题,修复(R)等问题 我是用的依赖版本如下 <dependency><groupId>org.apache.poi</grou…...

大学大模型教学:基于NC数据的全球气象可视化解决方案
引言 气象数据通常以NetCDF(Network Common Data Form)格式存储,这是一种广泛应用于科学数据存储的二进制文件格式。在大学气象学及相关专业的教学中,掌握如何读取、处理和可视化NC数据是一项重要技能。本文将详细介绍基于Python的NC数据处理与可视化解决方案,包含完整的代…...
Python学习(2) ----- Python的数据类型及其集合操作
在 Python 中,一切皆对象,每个对象都有类型。下面是 Python 中的常见内置类型分类和示例: 🟡 1. 数字类型(Numeric Types) 类型说明示例int整数5, -42float浮点数3.14, -0.5complex复数1 2j a 10 …...
机器学习算法-决策树
今天我们用一个 「相亲决策」 的例子来讲解决策树算法,保证你轻松理解原理和实现! 🌳 决策树是什么? 决策树就像玩 「20个问题」猜谜游戏: 你心里想一个东西(比如「苹果」) 朋友通过一系列问题…...

MediaMtx开源项目学习
这个博客主要记录MediaMtx开源项目学习记录,主要包括下载、推流(摄像头,MP4)、MediaMtx如何使用api去添加推流,最后自定义播放器,播放推流后的视频流,自定义Video播放器博客地址 1 下载 MediaMTX MediaMTX 提供了预编译的二进制文件,您可以从其 GitHub 页面下载: Gi…...

Linux安装EFK日志分析系统
目标:能够实现采集指定路径日志到es,用kibana实现日志分析 单es节点集群规划: 主机名IP 地址组件a1192.168.1.111Kibana elasticsearcha2192.168.1.112Fluentda3192.168.1.103Fluentd 1、安装Elasticsearch 1.1添加 Elastic 仓库并安装 E…...

Linux(9)——进程(控制篇——下)
目录 三、进程等待 1)进程等待的必要性 2)获取子进程的status 3)进程的等待方法 wait方法 waitpid方法 多进程创建以及等待的代码模型 非阻塞的轮训检测 四、进程程序替换 1)替换原理 2)替换函数 3&…...

E. Melody 【CF1026 (Div. 2)】 (求欧拉路径之Hierholzer算法)
E. Melody 思路 将所有出现过的音量和音高看作一个点,一个声音看作一条边,连接起来。那么很容易知道要找的就是图上的一条欧拉路径(类似一笔画问题) 又已知存在欧拉路径的充要条件为:度数为奇数的点的个数为0或者2个…...
@Pushgateway 数据自动清理
文章目录 Pushgateway 数据自动清理一、Pushgateway 数据清理的必要性二、自动清理方案方案1:使用带TTL功能的Pushgateway分支版本方案2:使用Shell脚本定期清理方案3:结合Prometheus记录规则自动清理 三、最佳实践建议四、验证与维护五、示例…...

粽叶飘香时 山水有相逢
粽叶飘香时 山水有相逢 尊敬的广大客户们: 五月初五,艾叶幽香。值此端午佳节,衡益科技全体同仁向您致以最诚挚的祝福! 这一年我们如同协同竞渡的龙舟,在数字化转型的浪潮中默契配合。每一次技术对接、每轮方案优化&a…...

YC-8002型综合变配电监控自动化系统
一 .系统概述 YC-8002型综合变配电监控自动化系统是西安亚川电力科技有限公司为适应广大客户要求,总结多项低 压配电网络自动化工程实例的经验,基于先进的电子技术、计算机和网络通讯等技术自主研发的--套结合本公司网络配电产品的应用于低压配电领域的…...

react diff 算法
diff 算法作为 Virtual DOM 的加速器,其算法的改进优化是 React 整个界面渲染的基础和性能的保障,同时也是 React 源码中最神秘的,最不可思议的部分 diff 算法会帮助我们就算出 VirtualDOM 中真正变化的部分,并只针对该部分进行原…...

近期手上的一个基于Function Grap(类AWS的Lambda)小项目的改造引发的思考
函数式Function是云计算里最近几年流行起来的新的架构和模式,因为它不依赖云主机,非常轻量,按需使用,甚至是免费使用,特别适合哪种数据同步,数据转发,本身不需要保存数据的业务场景,…...