当前位置: 首页 > news >正文

为什么需要 std::call_once?

std::call_once 是 C++ 标准库中的一个函数,用来确保某个操作仅被执行一次,通常用于线程安全的初始化操作。它常与 std::once_flag 结合使用,后者用于标记某个操作是否已经执行过。

为什么需要 std::call_once

在多线程程序中,我们有时需要确保某些操作在整个程序运行期间只执行一次。例如,初始化一个全局资源、配置或者其他某个全局性设置。普通的 if 语句并不足够保证线程安全,因为多个线程可能在同一时刻检测并试图执行该操作,从而导致多次执行同样的操作,造成潜在的错误。

std::call_once 保证了无论多少个线程尝试执行某个操作,那个操作只会在第一个线程执行时真正运行一次,后续线程不会再执行这个操作。

语法

#include <iostream>
#include <mutex>std::once_flag flag;  // once_flag 是一个标志,指示操作是否已经执行过void init()
{std::cout << "Initialization done." << std::endl;
}int main()
{// 保证 init 只会被执行一次std::call_once(flag, init);return 0;
}

代码讲解

  1. std::once_flag
    std::once_flag 是一个标志,它告诉 std::call_once 是否已经执行过某个操作。在第一次调用 std::call_once 时,它会标记这个操作已被执行。之后再调用 std::call_once 时,它会跳过这个操作,确保初始化只发生一次。

  2. std::call_once(flag, init)
    std::call_once 的第一个参数是一个 std::once_flag 类型的变量,它记录了操作是否执行过。第二个参数是你想执行的函数或可调用对象,这里是 init 函数。

    • 如果 flag 尚未设置,std::call_once 会调用 init() 函数。
    • 如果 flag 已经设置,后续线程对 std::call_once 的调用将不会再执行 init() 函数。
  3. 线程安全
    std::call_oncestd::once_flag 使得即使多个线程同时执行 std::call_once,也只会执行一次 init() 函数。多线程环境下的并发访问不会导致多次初始化。

示例:多线程中使用 std::call_once

一个常见的场景是在多线程中使用 std::call_once 来初始化资源,确保多个线程安全地共享资源。例如:

#include <iostream>
#include <thread>
#include <mutex>std::once_flag flag;void init()
{std::cout << "Initialization done." << std::endl;
}void thread_function()
{// 保证每个线程在初始化时调用一次 init()std::call_once(flag, init);std::cout << "Thread executed." << std::endl;
}int main()
{std::thread t1(thread_function);std::thread t2(thread_function);std::thread t3(thread_function);t1.join();t2.join();t3.join();return 0;
}

代码说明

  1. main() 函数中,我们创建了三个线程 t1, t2t3,并且它们都执行相同的 thread_function()
  2. 每个线程都会调用 std::call_once(flag, init),但是只有第一次调用时 init() 函数才会被执行,后续的线程将跳过该函数。
  3. 这样,尽管有多个线程,它们并不会重复执行 init(),确保了初始化操作仅执行一次。

std::call_once 与线程同步

std::call_once 依赖于 std::once_flag 来同步线程。当多个线程同时进入 std::call_once 时,只有一个线程能执行函数,其他线程将等待直到该操作完成。因此,它提供了一种简单且线程安全的方式来确保某些操作只执行一次。

适用场景

std::call_once 通常用于以下场景:

  • 线程安全的全局初始化:如初始化全局变量、单例模式中的实例化等。
  • 延迟初始化:当一个函数或操作不需要在程序启动时立即执行,而是需要在特定条件下执行一次时,std::call_once 提供了一个简洁的实现方式。
  • 库的初始化:比如在多个线程中初始化一些共享资源或者配置。

小结

  • std::call_once 是一个保证某个操作只执行一次的线程安全工具。
  • 它需要与 std::once_flag 一起使用,后者用来标记某个操作是否已经被执行过。
  • std::call_once 在多线程环境下能确保多个线程只会执行一次初始化操作,避免了竞态条件的出现。
http://www.lryc.cn/news/513402.html

相关文章:

  • ubuntu非root用户操作root权限问题-virbox挂在共享文件夹
  • 网络通讯协议
  • centos,789使用mamba快速安装devtools
  • 【人工智能机器学习基础篇】——深入详解强化学习之常用算法Q-Learning与策略梯度,掌握智能体与环境的交互机制
  • 银河麒麟桌面v10sp1修复引导笔记
  • 深入理解 MVCC 与 BufferPool 缓存机制
  • vue实现下拉多选、可搜索、全选功能
  • 探秘Kafka源码:关键内容解析
  • Android音频效果处理:基于`android.media.audiofx`包的原理、架构与实现
  • LeetCode - 初级算法 数组(两个数组的交集 II)
  • SQL 实战:分页查询的多种方式对比与优化
  • 汇川Easy系列正弦信号发生器(ST源代码)
  • JavaSpring AI与阿里云通义大模型的集成使用Java Data Science Library(JDSL)进行数据处理
  • Three.js教程002:Three.js结合Vue进行开发
  • pycharm+anaconda创建项目
  • vue2中遇到的问题与解决方案(自用)
  • CF2043b-B. Digits
  • ultralytics库RT-DETR代码解析
  • (七)- plane/crtc/encoder/connector objects
  • 基于STM32的四轴飞行器的控制系统(论文+源码)
  • 混合精度训练(Mixed Precision Training)中为什么在训练过程中不直接使用bf16进行权重更新?中英双语
  • 【java】HashMap的实现原理
  • FCM32F103C8T6开发指引
  • Python世界:人生苦短,我用Python
  • 【从零开始入门unity游戏开发之——C#篇43】C#补充知识——值类型和引用类型汇总补充、变量的生命周期与性能优化、值类型和引用类型组合使用
  • 从论文到实践:Stable Diffusion模型一键生成高质量AI绘画
  • 项目管理:用甘特图 “导航” 项目全程
  • v3.0.8- 「S+会员」新增专属运动秀,试试新穿搭吧- 与「好友」
  • 9-Gin 中自定义 Model --[Gin 框架入门精讲与实战案例]
  • 【VBA】EXCEL - VBA 创建 Sheet 表的 6 种方法,以及注意事项