96-只被执行一次的函数

本文内容以及后面的几篇文章都是在为 errno 的实现做铺垫,如果没有这些基础知识,你直接跳过去看原理可能会懵圈。

1. 问题提出

有时候,在执行多线程程序的时候,可能需要对某些共享资源进行初始化,一般来说,我们有两种方式:

  • 在创建线程之前进行初始化
  • 在线程函数中做初始化

对于第一种方式,肯定是最常用的了,不过有时候我们并不想这么做。因为一开始可能并不需要多线程程序,所以希望我们将多线程所使用的共享资源初始化动作推迟到线程函数中。如此将会导致一个问题,每个线程都执行初始化,岂不是被初始化很多次?

它的代码看起来像下面这样:

int *foo;
void thread_init() {
   foo = (int*) malloc(sizeof(int));
   ...
}

void *th_fn1(void *arg) {
   thread_init();
   ...
}

void *th_fn2(void *arg) {
   thread_init();
   ...
}

如果直接这样做的话, foo 指针就会被多次分配内存,导致错误。有没有一种办法,让 thread_init 只执行一次?根据前面我们学过的线程同步内容,我们可以采用标记加线程互斥来解决,不过,这太麻烦!

Linux 已经为我们准备了一套解决方案 —— pthread once.

2. pthread once

在这一套技术方案中,有两个基本要素:

  • 提供一个 pthread_once_t 类型全局对象,将其初始化为 PTHREAD_ONCE_INIT
  • 提供一个初始化函数,也就是前面的 thread_init 函数

接下来,只需要在每个线程函数中调用它所提供的初始化接口就行了。该初始化接口函数定义如下:

int phtread_once(pthread_once_t *initflag, void (*initfn)(void));

它的两个参数,就是上面讲到的两个基本要素。

接下来,我们用实验来讲清楚它的用法。

3. 程序清单

在 once 程序中,我们只证明,thread_init 函数只会被执行一次。

3.1 代码

// once.c
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>

pthread_once_t init_done = PTHREAD_ONCE_INIT;

// 只被执行一次的函数
void thread_init() {
  puts("I'm thread init");
}

void* fun(void *arg) {
  pthread_once(&init_done, thread_init);
  printf("Hello, I'm %s\n", (char*)arg);
  return NULL;
}


int main() {
  pthread_t tid1, tid2, tid3;
  pthread_create(&tid1, NULL, fun, "allen");
  pthread_create(&tid2, NULL, fun, "luffy");
  pthread_create(&tid3, NULL, fun, "zoro");
  pthread_join(tid1, NULL);
  pthread_join(tid2, NULL);
  pthread_join(tid3, NULL);
  return 0;
}

3.2 编译和运行

  • 编译和运行
$ gcc once.c -o once -lpthread
$ ./once
  • 运行结果


这里写图片描述
图1 thread_init 初始化函数只执行了一次

4. 总结

  • 知道 pthread once 是干嘛的
  • 掌握 pthread once
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值