Skip to Content
C++标准库源码解析call_once(GNU版本)

call_once

std::call_once 是 c++11 引入的新特性,主要用于函数或代码片段在多线程环境下,只需要执行一次。常见场景如 单例模式、init() 操作、一些系统参数的获取等。

头文件

  • <mutex>

使用示例(单例模式)

class SingleClass { public: static SingleClass* getInstance() { if (m_instance == nullptr) { std::call_once(m_instanceFlag, [this]() { m_instance = new SingleClass(); }); } return m_instance; } static SingleClass* m_instance; static std::once_flag m_instanceFlag; };

源码解析

call_once

内部调用 pthread_once 函数,pthread_once 函数在触发调用时实际调用 __once_proxy 函数

template<typename _Callable, typename... _Args> void call_once(once_flag& __once, _Callable&& __f, _Args&&... __args) { // 构造一个无参数的闭包函数,方便后续调用 auto __callable = [&] { std::__invoke(std::forward<_Callable>(__f), std::forward<_Args>(__args)...); }; // 预处理执行参数 once_flag::_Prepare_execution __exec(__callable); // 内部实际调用 pthread_once 函数 if (int __e = __gthread_once(&__once._M_once, &__once_proxy)) __throw_system_error(__e); } static inline int __gthread_once (__gthread_once_t *__once, void (*__func) (void)) { if (__gthread_active_p ()) return __gthrw_(pthread_once) (__once, __func); // 实际调用 pthread_once 函数 else return -1; }

once_flag::_Prepare_execution

主要用于将 __c 回调函数传递给 __once_callable,并构造完全闭包函数给 __once_call 函数指针

struct once_flag::_Prepare_execution { template<typename _Callable> explicit _Prepare_execution(_Callable& __c) { __once_callable = std::__addressof(__c); __once_call = [] { (*static_cast<_Callable*>(__once_callable))(); }; } ~_Prepare_execution() { // PR libstdc++/82481 __once_callable = nullptr; __once_call = nullptr; } _Prepare_execution(const _Prepare_execution&) = delete; _Prepare_execution& operator=(const _Prepare_execution&) = delete; };

__once_proxy、__once_callable、__once_call

__once_proxy、__once_callable、__once_call 定义在 libstdc++ 库的 mutex.cc 文件中,头文件中仅做了声明

// 使用线程局部存储(TLS),每个线程有单独的 __once_callable、__once_call 变量 // 因为线程按序执行,所以每个线程保证一份以下变量即可,每次调用新的 call_once 重新设置以下两个变量即可 __thread void* __once_callable; __thread void (*__once_call)(); extern "C" void __once_proxy() { __once_call(); }

pthread_once

该函数实际定义在 libpthread 库中,此处 init_routine 入参对应上述的 __once_proxy 函数

int __pthread_once (pthread_once_t *once_control, void (*init_routine) (void)) { atomic_full_barrier (); if (once_control->__run == 0) // 还没有执行过 { __pthread_spin_lock (&once_control->__lock); // 自旋锁上锁 if (once_control->__run == 0) // 再次判断是否已执行 { init_routine (); // 执行实际方法 atomic_full_barrier (); once_control->__run = 1; // 标记已执行 } __pthread_spin_unlock (&once_control->__lock); // 自旋锁解锁 } return 0; } strong_alias (__pthread_once, pthread_once);
最近更新于