97-线程私有变量

系统为每一个线程提供了一个私有“容器”,该容器中保存的是一个个的键值对。通过键可以获取值,也可以向此容器中保存值。pthread 中,对键的类型和值的类型都是有要求的。

1. 键类型及相关函数

键的类型是 pthread_key_t,它只能通过函数 pthread_key_create 进行初始化。它的定义如下:

int pthread_key_create(pthread_key_t *key, void (*destructor(void*));

键可被任意一个线程使用。

注意到上面的函数还有第二个参数,它需要传递一个析构函数,比如像下面这样:

void destructor(void* arg) {
   ...
}

当线程运行结束(return 或 pthread_exit)时,该函数会自动调用。析构函数的参数,是与该键关联的值(value).

pthread_key_create 第二个参数可以为空。

通过 pthread_key_delete 函数,可能删除指定的键:

int pthread_key_delete(pthread_key_t key);

注意,调用上面的函数,不会引起前面的析构函数的调用。

2. 保存键值对

线程容器中的值类型必须是 void*。可以通过下面的函数将键值对保存到线程自己的容器中。

int pthread_setspecific(pthread_key_t key, const void *value);

不同线程调用上面的函数时,只会将键值对保存到自己(指线程自己)的容器中。

所以,我们把这种键值对称为线程私有数据。

3. 根据键来获取值

可以通过下面的函数根据键来获取对应的值:

void* pthread_getspecific(pthread_key_t key);

注意,获取到的值类型仍然是 void* 类型。

4. 程序清单

程序 specdata 创建了两个线程,它们各自向自己的容器中保存了不同的数据,然后又从容器中将数据提取出来,并打印。

所有线程结束的时候,会调用 destructor 析构函数。

4.1 代码

// specdata.c
#include <stdio.h>
#include <pthread.h>

pthread_key_t key;

void destructor(void *arg) {
  printf("destructor: hello %d\n", (int)arg);
}

void *fun1(void *arg) {
  int data = 5;
  pthread_setspecific(key, (void*)data);
  int x = (int)pthread_getspecific(key);
  printf("fun1: x = %d\n", x); 
}

void *fun2(void *arg) {
  int data = 10; 
  pthread_setspecific(key, (void*)data);
  int x = (int)pthread_getspecific(key);
  printf("fun2: x = %d\n", x); 
}

int main() {
  pthread_t tid1, tid2;
  // 初始化 key
  pthread_key_create(&key, destructor);

  pthread_create(&tid1, NULL, fun1, NULL);
  pthread_create(&tid2, NULL, fun2, NULL);
  pthread_join(tid1, NULL);
  pthread_join(tid2, NULL);
  return 0;
}

4.2 编译和运行

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


这里写图片描述
图1 运行结果

4.3 结果分析

从图 1 可以看到,线程 fun1 和 fun2 中各自都向其容器中保存了不同的数据,然后又根据 key 将其提取出来并打印。线程运行结束后,会调用 destructor 函数,该函数的参数就是容器中的值。

实际上,如果一个线程容器中有多个键值对,当线程结束时,会多次调用 destructor 函数,调用的顺序和你初始化(pthread_key_create)键的顺序是一致的。

5. 总结

  • 知道线程私有变量是什么(线程私有容器中的键值对)
  • 掌握保存和获取键值对的方法

练习:在《只被执行一次的函数》一文中,你已经学会了在线程中调用初始化函数的方法,请你把本文实验中的键初始化流程改为使用 pthread_once 方法进行初始化。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值