1.一次性初始化 
在 Linux函数列表 中描述了Linux线程中的常用函数,这里详细讲解 pthread_once 函数的功能和使用。

(1)为何有“一次性初始化概念”出现? 
其实在开发中,很多事情都仅仅需要做一次,不管是什么。在主函数中并且在调用任何其他依赖于初始化的事物之前初始化应用是最为容易的,特别是在创建任何线程之前初始化它需要的数据,如 互斥量、条件变量、线程特定数据键等。

(2)静态初始化变量也很方便,为何pthread_onc 会出现? 
Linux线程开发中,通常对于 互斥量、条件变量等都会提供两种初始化方式,分别是动态初始化和静态初始化。如:

pthread_cond_t cond;
//动态初始化: pthread_cond_init(&cond, NULL);
//静态初始化: pthread_cond_t = PTHREAD_COND_INITIALIZER; pthread_mutex_t;
//动态初始化: pthread_mutex_init(&mutex,NULL);
//静态初始化: pthread_mutex_t = PTHREAD_MUTEX_INITIALIZER;

这里之所以出现 pthread_once 的主要原因是因为之前的 Linux Phread 不支持静态地初始化一个互斥量、条件变量等 。这样,要使用一个互斥量,就不得不首先调用pthread_mutex_init 初始化互斥量。而且必须仅仅初始化互斥量一次,因此初始化调用应在一次性初始化代码中进程。pthread_once解决了这个递归的问题。当互斥量的静态初始化被加到标准中时,pthread_once作为便利功能而被保留了下来。若使用pthread_once方便,就使用它,但是不必一定要使用它。

(3)pthread_once 如何使用? 
首先,来看看 pthread_once 的函数原型: 

/*确保初始化函数INIT_ROUTINE只被调用一次,即使pthread_once使用相同的ONCE_CONTROL
*参数执行了多次.ONCE_CONTROL必须指向一个初始化为PTHREAD_ONCE_INIT的静态或外部变量.
*初始化函数可能会抛出异常,这就是为什么这个函数没有被标记为__THROW.
*/
extern int pthread_once (pthread_once_t *__once_control,
void (*__init_routine) (void)) __nonnull ((1, 2));

根据该函数的定义: 
①首先,需要声明类型为 pthread_once_t 的一个控制变量,而且该控制变量必须使用PTHREAD_ONCE_INIT宏来静态的初始化。 
②必须创建一个包含与该 “控制变量(pthread_once_t)” 相关联的所有初始化代码函数,现在线程可以在任何时间调用pthread_once,指定一个指向一个控制变量的指针和指向相关初始化函数的指针。 

pthread_once函数首先检查控制变量,以判断是否已经完成初始化。如果已经完成,pthread_once简单的返回;否则,它回去调用初始化函数(没有参数),并且记录下初始化被完成。如果在一个线程初始化的时候,另外一个线程也调用了pthread_once,则调用线程将阻塞等待,知道那个线程完成初始化后返回。换言之,当调用pthread_once成功返回时,调用总是能够肯定所有的状态已经初始化完成。

代码1 
pthread_once初始化函数 threadOnceInit的主要功能是初始化互斥量。pthread_once的使用能够确保它只被初始化一次。 
线程执行函数subThread中,在调用互斥量之前,先去调用了pthread_once,是为了保证它即使在主函数中没有被创建,它也会存在。

/*************************************************************************
* File Name: pthread_once.c
* Author: The answer
* Function: Other
* Mail: 2412799512@qq.com
* Created Time: 2018年09月09日 星期日12时06分09秒
************************************************************************/ #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include <time.h>
#include <pthread.h>
#include <sys/select.h>
#include <sys/epoll.h>
#include <sys/poll.h>
#define STRUCT_INIT(l,r) .l = r
#define CHECK_VARIABLE(l,r) do{if(0 != r){fprintf(stderr,"[%s], err:[%d]\n",l,r);break;}}while(0); //宏初始化pthread_once_t控制变量
pthread_once_t once = PTHREAD_ONCE_INIT;
pthread_mutex_t mutex ;
static const int globalVal = 10; //pthread_once初始化函数
void threadOnceInit(){ int ret = -1;
ret = pthread_mutex_init(&mutex, NULL);
CHECK_VARIABLE("pthread_mutex_init error",ret);
puts("pthread_mutex_init success.");
return;
} //子线程执行函数
void* subThread(void* param){
printf("subThread to do...\n");
int ret = -1;
ret = pthread_once(&once,threadOnceInit); CHECK_VARIABLE("pthread_once error",ret);
ret = pthread_mutex_lock(&mutex);
CHECK_VARIABLE("pthread_mutex_lock err",ret);
printf("subThread has clocked the mutex. and globalVal is: [%d]\n",globalVal);
ret = pthread_mutex_unlock(&mutex);
CHECK_VARIABLE("pthread_mutex_unlock err.",ret); return NULL;
} int main(int argc,char **argv)
{
pthread_t Tid;
int ret = -1;
ret = pthread_create(&Tid,NULL,subThread, NULL);
CHECK_VARIABLE("pthread_create err.",ret); ret = pthread_once(&once,threadOnceInit);
CHECK_VARIABLE("pthread_once err",ret); ret = pthread_mutex_lock(&mutex);
CHECK_VARIABLE("pthread_mutex_lock",ret); printf("Main func has locked the mutex.\n");
ret = pthread_mutex_unlock(&mutex);
CHECK_VARIABLE("pthread_mutex_unlock",ret); ret = pthread_join(Tid,NULL);
CHECK_VARIABLE("pthread_join",ret);
return 0;
}

编译:gcc pthread_once.c -o a -lpthread 
执行:./a 
其结果为:

pthread_mutex_init success.
Main func has locked the mutex.
subThread to do...
subThread has clocked the mutex. and globalVal is: [10]

通过其打印结果可以看到:主函数先于子线程先执行。主函数中先调用pthread_once,此时mutex互斥量成功的被初始化,然后子线程中再去调用pthread_once时候,检测到已经完成了初始化操作,则立刻返回不执行。

LINUX线程之一次性初始化(PTHREAD_ONCE)的更多相关文章

  1. linux多线程学习笔记六--一次性初始化和线程私有数据【转】

    转自:http://blog.csdn.net/kkxgx/article/details/7513278 版权声明:本文为博主原创文章,未经博主允许不得转载. 一,一次性初始化 以保证线程在调用资源 ...

  2. [转载]Linux 线程实现机制分析

    本文转自http://www.ibm.com/developerworks/cn/linux/kernel/l-thread/ 支持原创.尊重原创,分享知识! 自从多线程编程的概念出现在 Linux ...

  3. Linux线程-创建

    Linux的线程实现是在内核以外来实现的,内核本身并不提供线程创建.但是内核为提供线程[也就是轻量级进程]提供了两个系统调用__clone()和fork (),这两个系统调用都为准备一些参数,最终都用 ...

  4. Linux线程学习(一)

    一.Linux进程与线程概述 进程与线程 为什么对于大多数合作性任务,多线程比多个独立的进程更优越呢?这是因为,线程共享相同的内存空间.不同的线程可以存取内存中的同一个变量.所以,程序中的所有线程都可 ...

  5. Linux线程学习(二)

    线程基础 进程 系统中程序执行和资源分配的基本单位 每个进程有自己的数据段.代码段和堆栈段 在进行切换时需要有比较复杂的上下文切换   线程 减少处理机的空转时间,支持多处理器以及减少上下文切换开销, ...

  6. Linux 线程与进程,以及通信

    http://blog.chinaunix.net/uid-25324849-id-3110075.html 部分转自:http://blog.chinaunix.net/uid-20620288-i ...

  7. 【转】 Linux 线程同步的三种方法

    线程的最大特点是资源的共享性,但资源共享中的同步问题是多线程编程的难点.linux下提供了多种方式来处理线程同步,最常用的是互斥锁.条件变量和信号量. 一.互斥锁(mutex) 通过锁机制实现线程间的 ...

  8. linux 线程编程详解

    1.线程的概念: 线程和进程有一定的相似性,通常称为轻量级的进程 同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等.但同一进程中的多个线程都有自身控制流 (它 ...

  9. Linux线程优先级

    转自:https://www.cnblogs.com/imapla/p/4234258.html Linux内核的三种调度策略: 1.SCHED_OTHER 分时调度策略 2.SCHED_FIFO   ...

  10. Linux 线程实现机制分析 Linux 线程模型的比较:LinuxThreads 和 NPTL

    Linux 线程实现机制分析 Linux 线程实现机制分析  Linux 线程模型的比较:LinuxThreads 和 NPTL http://www.ibm.com/developerworks/c ...

随机推荐

  1. Java Websocket 02: 原生模式通过 Websocket 传输文件

    目录 Java Websocket 01: 原生模式 Websocket 基础通信 Java Websocket 02: 原生模式通过 Websocket 传输文件 Websocket 原生模式 传输 ...

  2. 好的,以下是我为您拟定的自然语言处理(NLP)领域的100篇热门博客文章标题,以逻辑清晰、结构紧凑、简单易懂的

    目录 1. 引言 2. 技术原理及概念 3. 实现步骤与流程 4. 应用示例与代码实现讲解 1. 机器翻译 2. 文本分类 3. 情感分析 5. 优化与改进 6. 结论与展望 好的,以下是我为您拟定的 ...

  3. 【Azure 媒体服务】Azure Media Player 在Edge浏览器中不能播放视频问题的分析与解决

    问题描述 使用Azure Media Service 制作视频点播服务,在客户端使用 Azure Media Player 播放器在 Edge 浏览器中播放视频时候遇见无法播放的问题: 错误信息: T ...

  4. FreeFileSync结合任务计划实现T级数据的全量备份和每日十几G数据的增量自动备份

    1. 背景 公司现有nas存储中有共计1.8T左右的文件数据(一般是pdf.excel.图片.压缩文件等等格式),因为nas无法做备份:担心后面nas出现故障造成数据丢失,现急需一个解决方案实现如下目 ...

  5. 查看C语言程序对应的汇编代码

    在终端输入 gcc -S main.c 命令的意思是 编译不汇编 mian.c 可以换成想要汇编的C语言程序 然后生成 main.s 使用文本编辑器查看即可

  6. 4.10 x64dbg 反汇编功能的封装

    LyScript 插件提供的反汇编系列函数虽然能够实现基本的反汇编功能,但在实际使用中,可能会遇到一些更为复杂的需求,此时就需要根据自身需要进行二次开发,以实现更加高级的功能.本章将继续深入探索反汇编 ...

  7. 说说 Linux 的 curl 命令

    cURL,熟悉 Linux 的同学,没有人不知道这个命令吧:) 它有非常非常多的参数,我这里就不复制粘贴了,有需要可以 -h 或者谷歌搜索看看. 我从实用性的角度,说下我比较常用的几个参数: -v:啰 ...

  8. 一步一图带你构建 Linux 页表体系 —— 详解虚拟内存如何与物理内存进行映射

    笔者之前在自己的专栏<聊聊 Linux 内核> 里通过大量的篇幅写了一个系列关于内存管理相关的文章,在这个系列文章中,笔者分别通过虚拟内存管理和物理内存管理两个角度算是把 Linux 内存 ...

  9. Unity UGUI的RawImage(原始图片)组件的介绍及使用

    Unity UGUI的RawImage(原始图片)组件的介绍及使用 1. 什么是RawImage组件? RawImage是Unity UGUI中的一个组件,用于显示原始图片.与Image组件不同,Ra ...

  10. 【题解】Educational Codeforces Round 150(CF1841)

    赛时过了 A-E,然后就开摆了,为什么感觉 C 那么无厘头[发怒][发怒] 排名:25th A.Game with Board 题目描述: Alice 和 Bob 玩游戏,他们有一块黑板.最初,有 \ ...