std__call_once 的使用
文章目录
std::call_once
是 C++11 中引入的一个标准库函数,属于
<mutex>
头文件。它用于确保某个特定的函数(或代码块)在多线程程序中 只执行一次。这是一个线程安全的操作,通常用于实现 单例模式 或 延迟初始化 等场景。
1. 功能
std::call_once
确保某个操作或函数仅执行一次,无论多少线程同时执行该操作。它会使用一个 std::once_flag
对象来标识该操作是否已经执行过。如果在多个线程中同时调用 std::call_once
,只有第一个调用的线程会执行目标函数,其它线程会被阻塞,直到第一个线程执行完毕。
2. 语法
void std::call_once(std::once_flag& flag, Callable&& f, Args&&... args);
- flag: 一个
std::once_flag
类型的对象,用来标识是否已经执行过目标函数。 - f: 需要执行的目标函数(或可调用对象),可以是普通函数、函数指针、lambda 表达式等。
- args…: 传递给目标函数的参数。
3. std::once_flag
std::once_flag
是一个特殊的对象,用来标记目标函数是否已经被调用过。它的生命周期应该和 std::call_once
调用相同,且通常会在程序的全局范围内或某个共享资源中声明。
4. 工作原理
std::call_once
确保目标函数只执行一次,即使有多个线程同时调用。在第一次调用时,目标函数会被执行;随后调用 std::call_once
的线程会被阻塞,直到第一次调用完成。
5. 示例
5.1 简单示例
#include <iostream>
#include <mutex>
#include <thread>
std::once_flag flag;
void print_hello() {
std::cout << "Hello, world!" << std::endl;
}
void thread_function() {
std::call_once(flag, print_hello);
}
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;
}
在这个示例中,尽管有 3 个线程调用 std::call_once
,但是 print_hello()
只会执行一次,输出为:
Hello, world!
5.2 延迟初始化
std::call_once
常用于延迟初始化场景,比如在单例模式中初始化资源。
#include <iostream>
#include <mutex>
std::once_flag flag;
void initialize() {
std::cout << "Initialization done." << std::endl;
}
void do_work() {
std::call_once(flag, initialize); // 确保只执行一次初始化
std::cout << "Work is done." << std::endl;
}
int main() {
std::thread t1(do_work);
std::thread t2(do_work);
std::thread t3(do_work);
t1.join();
t2.join();
t3.join();
return 0;
}
在这个示例中,initialize()
函数只会被调用一次,即使有多个线程尝试执行它。
6. 应用场景
- 单例模式(Singleton Pattern):确保在多线程环境下,单例对象只初始化一次。
- 延迟初始化(Lazy Initialization):在程序启动时并不立即初始化某个资源,而是等到真正需要时再初始化,而且确保这个初始化操作只发生一次。
- 初始化共享资源:多个线程可能需要初始化某个共享资源,使用
std::call_once
可以保证该资源只会初始化一次。
7. 线程安全性
std::call_once
是线程安全的,它保证了多个线程同时调用时只会有一个线程成功执行目标函数。其他线程会被阻塞直到第一个线程完成执行。
8. 注意事项
std::call_once
的目标函数(即传给它的可调用对象)必须是 幂等 的,即函数应该能安全地多次执行,而不会产生副作用(在某些情况下,可能会多次调用std::call_once
,但每次调用目标函数之前都会被阻塞)。- 不能在目标函数中使用会导致死锁的操作,因为其他线程会在目标函数未执行完时被阻塞。
总结
std::call_once
是 C++11 提供的一个线程安全的机制,能够确保某个函数在多个线程中只执行一次。它通常与 std::once_flag
一起使用,用于实现线程安全的初始化、单例模式和延迟初始化等。