Linux线程之一次性初始化(pthread_once)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lixiaogang_theanswer/article/details/82557235

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:      [email protected] 
 * 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时候,检测到已经完成了初始化操作,则立刻返回不执行。

猜你喜欢

转载自blog.csdn.net/lixiaogang_theanswer/article/details/82557235