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. cmake 安装一个目录下的图片 到另一个目录文件中去

    install(DIRECTORY ./cfg/labels/ DESTINATION ./fservo/cfg/yolo_cfg/labels/) install (DIRECTORY ./cfg/ ...

  2. 【电脑Tips】Win11自动更新之后开机黑屏

    目录 0.问题描述 1. 释放静电 具体操作 效果 参考博客 2. 运行explorer.exe 具体操作: [问题]:如何打开任务管理器? 效果 参考博客 另外的运行方法 3. 禁用APP Read ...

  3. 通过模仿学会Python爬虫(一):零基础上手

    好家伙,爬虫来了 爬虫,这玩意,不会怎么办, 诶,先抄一份作业回来 1.别人的爬虫  Python爬虫史上超详细讲解(零基础入门,老年人都看的懂)_ChenBinBini的博客-CSDN博客 # -* ...

  4. 7. RESTful

    1. RESTful简介 ‍ REST:Representational State Transfer,表现层资源状态转移. ①资源 ‍ 资源是一种看待服务器的方式,即,将服务器看作是由很多离散的资源 ...

  5. 响应式编程:Vert.x官网学习

    本文基于 Vert.x 官网 https://vertx.io/ 内容,带领大家学习响应式编程里比较有名的工具包 Vert.x .文章内容取自官网由博主简化总结,希望帮助大家理解响应式编程. Vert ...

  6. 在HTML中引入React和JSX

    前言 Vue 可以非常方便地与 Pure HTML 结合,代替 jQuery 的功能,有一次遇到类似的场景时,我就想 React 能不能也以这种方式接入 HTML 网页,从而提高开发效率. 结果当然是 ...

  7. react项目中使用plop

    第一步,安装依赖 npm install plop --dev //或者用yarn yarn add plop --dev 第二步,在package同级目录下新建plopfile.js 这是plop的 ...

  8. 删除 /tmp 目录下长时间未访问的文件

    #!/bib/bash DIR=/tmp cd $DIR || { echo "Dir not Found: $Dir" exit } echo "Delete a fi ...

  9. Cesium-加载3D飞机模型沿指定路线前进

    https://blog.csdn.net/Apple_Coco/article/details/108882146

  10. 小白终于解决了在学习Go中不知道Makefile是什么的难题

    如何在Go中使用Makefile 1.Makefile是什么 Makefile是一种构建工具,用于在项目中定义和执行一系列命令.它通常包含了一些规则和目标,用于编译.测试.运行和清理项目. 2.Mak ...