pthread_once函数使用场景与原理

 更新时间:2025年12月25日 11:18:34   作者:RedmiUltra  
pthread_once 是 POSIX 线程库中的一个函数,主要用于确保某个初始化操作在多线程环境中只执行一次,即使多个线程同时尝试执行该操作,本文就来介绍一下pthread_once函数使用场景与原理,感兴趣的可以了解一下

pthread_once 是 POSIX 线程库中的一个函数,主要用于确保某个初始化操作在多线程环境中只执行一次,即使多个线程同时尝试执行该操作。其核心设计目标是提供线程安全的、高效的一次性初始化机制。

函数原型

#include <pthread.h>
int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));
  • once_control:指向 pthread_once_t 类型变量的指针(需初始化为 PTHREAD_ONCE_INIT)。
  • init_routine:指向初始化函数的指针(无参数、无返回值)。
  • 返回值:成功返回 0,失败返回错误码。

核心作用与原理

  1. 线程安全的一次性执行
    无论有多少线程调用 pthread_onceinit_routine 函数只会被执行一次(由第一个到达的线程执行)。
  2. 同步机制
    后续调用的线程会阻塞等待,直到初始化函数执行完毕,然后直接返回。
  3. 避免竞态条件
    无需额外锁机制即可保证初始化操作的原子性。

典型使用场景

1. 全局资源的初始化

#include <pthread.h>
#include <stdio.h>

// 全局初始化控制变量
static pthread_once_t once_control = PTHREAD_ONCE_INIT;
static int global_data;

void init_global_data() {
    global_data = 42; // 初始化全局数据
    printf("Global data initialized!\n");
}

void* thread_func(void* arg) {
    pthread_once(&once_control, init_global_data); // 安全初始化
    printf("Thread %ld uses global_data=%d\n", (long)arg, global_data);
    return NULL;
}

int main() {
    pthread_t t1, t2;
    pthread_create(&t1, NULL, thread_func, (void*)1);
    pthread_create(&t2, NULL, thread_func, (void*)2);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    return 0;
}

输出(初始化仅一次):

Global data initialized!
Thread 1 uses global_data=42
Thread 2 uses global_data=42

2. 单例模式实现

// 线程安全的单例初始化
Singleton* get_instance() {
    static pthread_once_t once = PTHREAD_ONCE_INIT;
    static Singleton* instance = NULL;
    
    void init_singleton() {
        instance = malloc(sizeof(Singleton));
        // ...初始化单例...
    }
    
    pthread_once(&once, init_singleton);
    return instance;
}

3. 延迟初始化(Lazy Initialization)

// 按需初始化全局配置
void load_config() {
    static pthread_once_t once = PTHREAD_ONCE_INIT;
    pthread_once(&once, read_config_file); // 首次调用时读取配置文件
    // 使用配置...
}

4. 库的初始化

// 动态库中安全初始化内部状态
void lib_function() {
    static pthread_once_t lib_init_once = PTHREAD_ONCE_INIT;
    pthread_once(&lib_init_once, internal_lib_init);
    // ...其他操作...
}

关键注意事项

  1. once_control 必须静态初始化

    pthread_once_t once_control = PTHREAD_ONCE_INIT; // 正确
    

    动态初始化(如运行时赋值)会导致未定义行为。

  2. 不可重置状态
    once_control 的状态是永久的,初始化完成后无法再次触发。

  3. 避免递归调用
    不要在 init_routine 中嵌套调用 pthread_once,可能导致死锁。

  4. 错误处理
    init_routine 崩溃,后续线程会因等待而阻塞。需确保初始化函数健壮性。

替代方案对比

方法优点缺点
pthread_once无锁、高效、简洁状态不可重置
互斥锁 + 标志位灵活(可重试、可重置)每次调用需加锁,性能较低
C11 call_once跨平台(C/C++标准)需支持 C11 标准

总结

使用场景
✅ 需要线程安全的一次性初始化(如全局变量、单例、库状态)。
✅ 希望避免显式加锁的开销。
✅ 延迟初始化资源提升性能。

核心优势
通过内核/编译器级优化,以最小代价实现线程安全的初始化,是 POSIX 多线程编程中的重要同步原语。

到此这篇关于pthread_once函数使用场景与原理的文章就介绍到这了,更多相关pthread_once函数使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C语言中的指针以及二级指针代码详解

    C语言中的指针以及二级指针代码详解

    这篇文章主要介绍了C语言中的指针以及二级指针代码详解,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下
    2018-01-01
  • Opencv基于CamShift算法实现目标跟踪

    Opencv基于CamShift算法实现目标跟踪

    这篇文章主要为大家详细介绍了Opencv基于CamShift算法实现目标跟踪,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-01-01
  • 关于C语言strlen与sizeof区别详情

    关于C语言strlen与sizeof区别详情

    对于 strlen 和 sizeof,相信不少程序员会混淆其功能。虽然从表面上看它们都可以求字符串的长度,但二者却存在着许多不同之处及本质区别,今天得这篇文章我们就来学习C语言strlen与sizeof区别的相关资料,需要的朋友可以参考一下
    2021-10-10
  • C++ 读取文件内容到指定类型的变量方法

    C++ 读取文件内容到指定类型的变量方法

    今天小编就为大家分享一篇C++ 读取文件内容到指定类型的变量方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-07-07
  • C语言详解结构体的内存对齐与大小计算

    C语言详解结构体的内存对齐与大小计算

    C 数组允许定义可存储相同类型数据项的变量,结构是 C 编程中另一种用户自定义的可用的数据类型,它允许你存储不同类型的数据项,本篇让我们来了解C 的结构体内存对齐与计算大小
    2022-04-04
  • C语言数据结构与算法之图的遍历(二)

    C语言数据结构与算法之图的遍历(二)

    这篇文章主要是介绍了利用广度优先算法实现图的遍历,文中利用图文详细的介绍了实现步骤,对我们学习数据结构与算法有一定的帮助,需要的朋友可以参考一下
    2021-12-12
  • 详解桶排序算法的思路及C++编程中的代码实现

    详解桶排序算法的思路及C++编程中的代码实现

    桶排序即是先把每个桶中的元素进行排序然后遍历桶依次列出元素的算法,桶排序在元素较少的情况下很高效,以下我们就来详解桶排序算法的思路及C++编程中的代码实现:
    2016-07-07
  • C++基于Directx MMX实现的图像灰度转换代码

    C++基于Directx MMX实现的图像灰度转换代码

    这篇文章主要介绍了C++基于Directx MMX实现的图像灰度转换代码,需要的朋友可以参考下
    2014-08-08
  • Visual studio2022 利用glfw+glad配置OpenGL环境的详细过程

    Visual studio2022 利用glfw+glad配置OpenGL环境的详细过程

    这篇文章主要介绍了Visual studio2022 利用glfw+glad配置OpenGL环境,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-10-10
  • C语言 链式二叉树结构详解原理

    C语言 链式二叉树结构详解原理

    二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址
    2021-11-11

最新评论