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;
}
代码讲解
-
std::once_flag
:
std::once_flag
是一个标志,它告诉std::call_once
是否已经执行过某个操作。在第一次调用std::call_once
时,它会标记这个操作已被执行。之后再调用std::call_once
时,它会跳过这个操作,确保初始化只发生一次。 -
std::call_once(flag, init)
:
std::call_once
的第一个参数是一个std::once_flag
类型的变量,它记录了操作是否执行过。第二个参数是你想执行的函数或可调用对象,这里是init
函数。- 如果
flag
尚未设置,std::call_once
会调用init()
函数。 - 如果
flag
已经设置,后续线程对std::call_once
的调用将不会再执行init()
函数。
- 如果
-
线程安全:
std::call_once
和std::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;
}
代码说明
- 在
main()
函数中,我们创建了三个线程t1
,t2
和t3
,并且它们都执行相同的thread_function()
。 - 每个线程都会调用
std::call_once(flag, init)
,但是只有第一次调用时init()
函数才会被执行,后续的线程将跳过该函数。 - 这样,尽管有多个线程,它们并不会重复执行
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
在多线程环境下能确保多个线程只会执行一次初始化操作,避免了竞态条件的出现。