为什么需要 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 在多线程环境下能确保多个线程只会执行一次初始化操作,避免了竞态条件的出现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

pumpkin84514

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值