std::call_once的原理及使用

 基本概念   

  std::call_once 是 C++11 中引入的一个模版函数,实现多线程环境下实现单次调用,避免重复执行同一操作。

std::call_once 的原理

  std::call_once 的设计目的是为了保证某个操作在多线程程序中只被调用一次。它结合了std::mutex 和一个称为“标志”(std::once_flag)的机制来标记某个操作是否已经执行过。只有当标志没有被设置时,操作才会被执行。其他线程会等待该操作完成,确保只执行一次,确保在程序的不同线程中,只有第一个调用该操作的线程会执行操作,其他线程会等待直到该操作完成。

std::call_once 的用法

std::call_once 接受两个参数:

  • once_flag:一个std::once_flag 类型的对象,用于标记该操作是否已经执行过。

  • Function:一个可调用对象(如函数、函数指针、lambda 表达式等),这是要执行的操作。

        在以下的例子中,尽管有三个线程调用std::call_once,但是init 只有在第一次调用时执行一次。其他线程等待操作完成后就会跳过该操作。 

#include <iostream>
#include <thread>
#include <mutex>

std::once_flag flag;

void init()
{
    std::cout << "初始化完成!" << std::endl;
}

void thread_func()
{
    std::call_once(flag, init);  // 保证 init只执行一次
}

int main()
{
    std::thread t1(thread_func);
    std::thread t2(thread_func);
    std::thread t3(thread_func);
    
    t1.join();
    t2.join();
    t3.join();

    return 0;
}

使用场景

线程安全的懒加载

        在多线程环境下,如果需要对某个全局或静态变量进行初始化,而只希望该初始化操作执行一次,std::call_once 是一个理想的选择。它确保只有一个线程执行初始化,其他线程等待,避免重复初始化。

      例如libcURL 库的线程安全初始化,在使用libcURL 进行网络请求时,库的全局初始化操作(如curl_global_init())不是线程安全的,而进行数据请求的操作却是线程安全的。这种情况下,我们可以使用std::call_once 来确保全局初始化只执行一次,无论在哪个线程中调用,也无需担心并发访问。

#include <iostream>
#include <thread>
#include <mutex>
#include <curl/curl.h>

std::once_flag curl_flag;

void initialize_curl()
{
    if (curl_global_init(CURL_GLOBAL_DEFAULT) != 0)
    {
        throwstd::runtime_error("curl_global_init failed!");
    }
    std::cout << "libcURL initialized!" << std::endl;
}

void visite_webSite(const std::string& url)
{
    // 保证 curl_global_init 只被调用一次
    std::call_once(curl_flag, initialize_curl);

    // 创建 CURL 对象
    CURL* curl = curl_easy_init();
    if (curl)
    {
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        CURLcode res = curl_easy_perform(curl);
        if (res == CURLE_OK)
        {
            std::cout << "Fetched data from: " << url << std::endl;
        }
        else
        {
            std::cerr << "CURL error: " << curl_easy_strerror(res) << std::endl;
        }
        curl_easy_cleanup(curl);
    }
}

int main()
{
    std::thread t1(visite_webSite, "https://www.baidu.com");
    std::thread t2(visite_webSite, "https://blog.csdn.net/WSTONECH/article/details");

    t1.join();
    t2.join();

    return 0;
}

资源释放与清理

        在一些应用场景中,你可能希望在线程结束时执行某些清理操作。std::call_once 可确保这些清理工作只会在第一次触发时执行。

确保某些操作的线程安全执行

        如果某些操作需要跨多个线程并且保证线程安全(例如配置设置的初始化、数据库连接池的创建等),std::call_once 提供了一种简单、有效的方式来避免竞争条件。

项目使用注意事项:

   std::call_once 内部实现使用了线程同步机制,如互斥量或其他轻量级同步原语。尽管它确保了操作只执行一次,但如果频繁调用std::call_once,可能会产生一定的性能开销。因此,在性能要求较高的场景中,尽量减少std::call_once 的调用次数,并通过优化同步策略来降低开销。 

          例如libcurl库的全局初始化,没有必要懒加载。如果使用std::call_once写在网络请求的通用函数中固然可行,但是每一次网络访问都会产生同步开销,但是调整代码结构,将库的全局初始化挪到软件初始化的地方(仅调用一次),则软件性能会更好一点。

    例如接入项目中的各种LED、LCD、扫码枪、票箱和相机等等设备大部分需要进行初始化,在之前的老旧项目中初始化和资源释放等直接拷贝设备代码和数据发送写在一个函数中,通过std::call_once进行多线程中初始化和SDK资源释放操作仅执行一次的保证,但是在大型车厂中过车频繁,显示节目多样,会严重拖慢软件的性能;后续优化将设备SDK和动态库初始化转移到软件初始化函数中,资源释放单独写一个全局的软件释放函数,在软件退出时进行统一释放,提高了软件的运行效率。

   一般能通过调整代码结构避免多线程竞争的代码没必要使用std::call_once,如果必须懒加载、必须在多线程中同步,调用频次较低的场景可以考虑使用std::call_once进行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/962893.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Pytorch和Keras】使用transformer库进行图像分类

目录 一、环境准备二、基于Pytorch的预训练模型1、准备数据集2、加载预训练模型3、 使用pytorch进行模型构建 三、基于keras的预训练模型四、模型测试五、参考 现在大多数的模型都会上传到huggface平台进行统一的管理&#xff0c;transformer库能关联到huggface中对应的模型&am…

c语言进阶(简单的函数 数组 指针 预处理 文件 结构体)

c语言补充 格式 void函数头 {} 中的是函数体 sum函数名 &#xff08;&#xff09; 参数表 #include <stdio.h>void sum(int begin, int end) {int i;int sum 0;for (i begin ; i < end ; i) {sum i;}printf("%d到%d的和是%d\n", begin, end, sum); …

比较器使用

1 比较器是什么 2 使用比较器的注意事项 输出是否要接上拉电阻 2.1 推挽结构&#xff08;可以不用加上拉电阻&#xff09; 2.2 开漏结构&#xff08;OD,OC&#xff09;需要加上拉电阻 2.3 LM393 2.4 推挽和开漏 3 比较器的注意事项 输出是否要接上拉电阻 3.1 比较器内部有吸…

爬虫基础(三)Session和Cookie讲解

目录 一、前备知识点 &#xff08;1&#xff09;静态网页 &#xff08;2&#xff09;动态网页 &#xff08;3&#xff09;无状态HTTP 二、Session和Cookie 三、Session 四、Cookie &#xff08;1&#xff09;维持过程 &#xff08;2&#xff09;结构 正式开始说 Sessi…

项目升级Sass版本或升级Element Plus版本遇到的问题

项目升级Sass版本或升级Element Plus版本遇到的问题 如果项目有需求需要用到高版本的Element Plus组件&#xff0c;则需要升级相对应的sass版本&#xff0c;Element 文档中有提示&#xff0c;2.8.5及以后得版本&#xff0c;sass最低支持的版本为1.79.0&#xff0c;所升级sass、…

数据结构-Stack和栈

1.栈 1.1什么是栈 栈是一种特殊的线性表&#xff0c;只允许在固定的一段进行插入和删除操作&#xff0c;进行插入和删除操作的一段称为栈顶&#xff0c;另一端称为栈底。 栈中的数据元素遵顼后进先出LIFO&#xff08;Last In First Out&#xff09;的原则&#xff0c;就像一…

stm32硬件实现与w25qxx通信

使用的型号为stm32f103c8t6与w25q64。 STM32CubeMX配置与引脚衔接 根据stm32f103c8t6引脚手册&#xff0c;采用B12-B15四个引脚与W25Q64连接&#xff0c;实现SPI通信。 W25Q64SCK&#xff08;CLK&#xff09;PB13MOSI&#xff08;DI&#xff09;PB15MISO(DO)PB14CS&#xff08…

04树 + 堆 + 优先队列 + 图(D1_树(D8_B*树(B*)))

目录 一、基本介绍 二、相同思想和策略 三、不同的方式的磁盘空间利用 四、知识小结 一、基本介绍 B*树是Btree的变体&#xff0c;在B树的基础上(所有的叶子结点中包含了全部关键字的信息&#xff0c;及指向含有 这些关键字记录的指针)&#xff0c; B*树中非根和非叶子结…

Hot100之哈希

1两数之和 题目 思路解析 解法1--两次循环 解法2--哈希表一次循环 代码 解法1--两次循环 class Solution {public int[] twoSum(int[] nums, int target) {int nums1[] new int[2];int length nums.length;for (int i 0; i < length; i) {for (int j i 1; j < …

Autosar-以太网是怎么运行的?(原理部分)

写在前面&#xff1a; 入行一段时间了&#xff0c;基于个人理解整理一些东西&#xff0c;如有错误&#xff0c;欢迎各位大佬评论区指正&#xff01;&#xff01;&#xff01; 1.TCP/IP协议详解 TCP/IP协议包含了一系列的协议&#xff0c;也叫TCP/IP协议族&#xff08;TCP/IP P…

2025年大数据毕业设计选题推荐:数据分析与可视化 数据挖掘

目录 前言 毕设选题 开题指导建议 更多精选选题 选题帮助 最后 前言 大家好,这里是海浪学长毕设专题! 大四是整个大学期间最忙碌的时光&#xff0c;一边要忙着准备考研、考公、考教资或者实习为毕业后面临的升学就业做准备,一边要为毕业设计耗费大量精力。学长给大家整理…

如何在vs2022中处理python下无法输出中文问题

1.如何在vs2022中处理python下无法输出中文问题 进入vs界面时---工具菜单---自定义----在自定义窗口下选中”命令”页面----在菜单栏内----选择文件----再点击添加命令----左侧栏下滑找到文件-----在右侧往下拉找到并点击高级保存选项----再点击确定。 此时VS工程页面上会出现…

SSRF 漏洞利用 Redis 实战全解析:原理、攻击与防范

目录 前言 SSRF 漏洞深度剖析 Redis&#xff1a;强大的内存数据库 Redis 产生漏洞的原因 SSRF 漏洞利用 Redis 实战步骤 准备环境 下载安装 Redis 配置漏洞环境 启动 Redis 攻击机远程连接 Redis 利用 Redis 写 Webshell 防范措施 前言 在网络安全领域&#xff0…

UniApp开发的微信小程序主包过大问题及解决方案 编译小程序时Node-modules被打入主包

欢迎关注 『开发必备』 专栏,专注于解决你在开发过程中遇到的各种问题,帮你快速找到解决方案,节省大量调试时间。内容持续更新中,保证每篇都值得收藏! UniApp开发的微信小程序主包过大问题及解决方案 在使用UniApp开发微信小程序时,很多开发者都会遇到一个问题:打包后,…

Diffusion--人工智能领域的革命性技术

在人工智能领域&#xff0c;“diffusion”一词通常指的是“扩散模型”&#xff08;Diffusion Models&#xff09;&#xff0c;其全称为“Denoising Diffusion Probabilistic Models”&#xff08;DDPMs&#xff09;。扩散模型是一类生成式模型&#xff0c;它通过逐步去噪的方式…

升级到Mac15.1后pod install报错

升级Mac后&#xff0c;Flutter项目里的ios项目运行 pod install报错&#xff0c; 遇到这种问题&#xff0c;不要着急去百度&#xff0c;大概看一下报错信息&#xff0c;每个人遇到的问题都不一样。 别人的解决方法并不一定适合你&#xff1b; 下面是报错信息&#xff1a; #…

基于 oneM2M 标准的空气质量监测系统的互操作性

论文标题 英文标题&#xff1a; Interoperability of Air Quality Monitoring Systems through the oneM2M Standard 中文标题&#xff1a; 基于 oneM2M 标准的空气质量监测系统的互操作性 作者信息 Jonnar Danielle Diosana, Gabriel Angelo Limlingan, Danielle Bryan Sor…

利用Muduo库实现简单且健壮的Echo服务器

一、muduo网络库主要提供了两个类&#xff1a; TcpServer&#xff1a;用于编写服务器程序 TcpClient&#xff1a;用于编写客户端程序 二、三个重要的链接库&#xff1a; libmuduo_net、libmuduo_base、libpthread 三、muduo库底层就是epoll线程池&#xff0c;其好处是…

四.3 Redis 五大数据类型/结构的详细说明/详细使用( hash 哈希表数据类型详解和使用)

四.3 Redis 五大数据类型/结构的详细说明/详细使用&#xff08; hash 哈希表数据类型详解和使用&#xff09; 文章目录 四.3 Redis 五大数据类型/结构的详细说明/详细使用&#xff08; hash 哈希表数据类型详解和使用&#xff09;2.hash 哈希表常用指令(详细讲解说明)2.1 hset …

苍穹外卖第一天

角色分工 技术选型 pojo子模块 nginx反向代理 MD5密码加密