C++: std::once_flag 和 std::call_once

简介: `std::once_flag` 和 `std::call_once` 是 C++11 引入的同步原语,确保某个函数在多线程环境中仅执行一次。

std::once_flag 和 std::call_once

std::once_flag 和 std::call_once 是 C++11 引入的同步原语,用于确保某个函数在多线程环境中只被执行一次。它们位于 头文件中,主要用于实现线程安全的初始化操作。

std::once_flag

概述

类型:std::once_flag 是一个结构体,用于记录某个函数是否已经被调用过。
用途:与 std::call_once 配合使用,确保某个函数在多线程环境中只被执行一次。

std::once_flag my_flag;

std::call_once

函数:std::call_once 是一个函数模板,用于确保某个函数在多线程环境中只被执行一次。

参数:
std::once_flag& flag:一个 std::once_flag 对象,用于记录函数是否已经被调用过。

Function&& f:要执行的函数或可调用对象。

Args&&... args:传递给函数的参数。

std::once_flag my_flag;

void initialize() {
    // 初始化代码
}

void some_function() {
    std::call_once(my_flag, initialize);
}

示例代码

以下是一个完整的示例,展示了如何使用 std::once_flag 和 std::call_once 来确保某个函数在多线程环境中只被执行一次:

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

std::once_flag my_flag;

void initialize() {
    std::cout << "Initialization code executed" << std::endl;
}

void thread_function() {
    std::call_once(my_flag, initialize);
}

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;
}

底层实现

std::once_flag 和 std::call_once 的底层实现依赖于操作系统提供的同步机制,通常使用互斥锁(mutex)和条件变量(condition variable)来确保线程安全。

std::once_flag

std::once_flag 内部维护一个状态,用于记录函数是否已经被调用过。
这个状态通常是一个原子变量(atomic variable),确保在多线程环境下的操作是原子的。


std::call_once

std::call_once 内部使用一个互斥锁来保护对 std::once_flag 状态的访问。
如果函数尚未被调用,std::call_once 会锁定互斥锁,执行函数,然后更新 std::once_flag 的状态。
如果函数已经被调用,std::call_once 会立即返回,不会再次执行函数。
相关文章
|
3月前
|
存储 前端开发 安全
C++一分钟之-未来与承诺:std::future与std::promise
【6月更文挑战第27天】`std::future`和`std::promise`是C++异步编程的关键工具,用于处理未完成任务的结果。`future`代表异步任务的结果容器,可阻塞等待或检查结果是否就绪;`promise`用于设置`future`的值,允许多线程间通信。常见问题包括异常安全、多重获取、线程同步和未检查状态。解决办法涉及智能指针管理、明确获取时机、确保线程安全以及检查未来状态。示例展示了使用`std::async`和`future`执行异步任务并获取结果。
54 2
|
2月前
|
存储 C++ 运维
开发与运维函数问题之使用C++标准库中的std::function来简化回调函数的使用如何解决
开发与运维函数问题之使用C++标准库中的std::function来简化回调函数的使用如何解决
38 6
|
2月前
|
C++ 运维
开发与运维编译问题之在C++中在使用std::mutex后能自动释放锁如何解决
开发与运维编译问题之在C++中在使用std::mutex后能自动释放锁如何解决
46 2
|
3月前
|
安全 C++
C++一分钟之-字符串处理:std::string
【6月更文挑战第25天】`std::string`是C++文本处理的核心,存在于`&lt;string&gt;`库中。它支持初始化、访问、连接、查找、替换等操作。常见问题包括空指针解引用、越界访问和不当内存管理。要安全使用,确保字符串初始化,用`at()`检查边界,用`.empty()`检查空字符串,且无需手动释放内存。高效技巧包括预先分配内存、利用互转函数以及使用迭代器。记得正确比较和遍历字符串以保证代码效率和安全性。
58 5
|
3月前
|
存储 设计模式 安全
C++一分钟之-并发编程基础:线程与std::thread
【6月更文挑战第26天】C++11的`std::thread`简化了多线程编程,允许并发执行任务以提升效率。文中介绍了创建线程的基本方法,包括使用函数和lambda表达式,并强调了数据竞争、线程生命周期管理及异常安全等关键问题。通过示例展示了如何用互斥锁避免数据竞争,还提及了线程属性定制、线程局部存储和同步工具。理解并发编程的挑战与解决方案是提升程序性能的关键。
64 3
|
3月前
|
C++
c++中的using namespace std;
c++中的using namespace std;
|
4月前
|
编译器 C语言 C++
【C++的奇迹之旅(二)】C++关键字&&命名空间使用的三种方式&&C++输入&输出&&命名空间std的使用惯例
【C++的奇迹之旅(二)】C++关键字&&命名空间使用的三种方式&&C++输入&输出&&命名空间std的使用惯例
|
4月前
|
C++
【C++】std::string 转换成非const类型 char* 的三种方法记录
【C++】std::string 转换成非const类型 char* 的三种方法记录
63 0
|
14天前
|
存储 编译器 C++
C ++初阶:类和对象(中)
C ++初阶:类和对象(中)
|
1月前
|
存储 安全 编译器
【C++】类和对象(下)
【C++】类和对象(下)
【C++】类和对象(下)