Linux线程私有数据Thread-specific Data(TSD) 详解
Linux线程私有数据Thread-specific Data详解
- 前言
- 线程私有数据
- TSD概念
- Posix api细节探究
- pthread_key_create:创建一个键
- pthread_key_delete:注销一个键
- pthread_setspecific:为指定key 设置线程私有数据 val
- pthread_getspecific:从指定键读取线程的私有数据
- 一图诠释底层数据结构的关联
- 跑个demo看看效果
前言
本文将详细介绍pthread_key的用法以及pthread_key的原理。pthread_key在《ntyco协程》中,以及后续文章《try catch的实现》都有用到。跟我一起学习的读者务必搞懂原理。
本专栏知识点是通过零声教育的线上课学习,进行梳理总结写下文章,对c/c++linux课程感兴趣的读者,可以点击链接 C/C++后台高级服务器课程介绍 详细查看课程的服务。
线程私有数据
TSD概念
在多线程的程序中,所有的线程都可以使用和修改定义在全局的全局变量。也就是说全局变量被所有的线程共有。有没有一种办法,使得这个"全局变量",被线程独有,在线程的内部,该“全局变量”可以被线程的各个函数接口访问,但是对其他线程屏蔽呢?换句话说,本文后续要介绍的,就是线程私有数据,即 表面看起来是一个“全局变量”,所有的线程都可以使用它,但是每个线程都是独享它的,它的值在每一个线程中都是单独存储的。
线程私有数据(TSD):同名而不同值,即同key不同value,一键多值,所以对私有数据的访问都是通过键来访问到value的。
Posix api细节探究
Posix定义了两个API来创建和销毁TSD,以及两个API来设置与访问TSD
/* Functions for handling thread-specific data. *//* Create a key value identifying a location in the thread-specificdata area. Each thread maintains a distinct thread-specific dataarea. DESTR_FUNCTION, if non-NULL, is called with the valueassociated to that key when the key is destroyed.DESTR_FUNCTION is not called if the value associated is NULL whenthe key is destroyed. */
extern int pthread_key_create (pthread_key_t *__key,void (*__destr_function) (void *))__THROW __nonnull ((1));/* Destroy KEY. */
extern int pthread_key_delete (pthread_key_t __key) __THROW;/* Return current value of the thread-specific data slot identified by KEY. */
extern void *pthread_getspecific (pthread_key_t __key) __THROW;/* Store POINTER in the thread-specific data slot identified by KEY. */
extern int pthread_setspecific (pthread_key_t __key,const void *__pointer) __THROW ;
pthread_key_create:创建一个键
int pthread_key_create(pthread_key_t *key, void (*destr_function) (void*));
首先从Linux的TSD池中分配一项,然后将其值赋给key供以后访问使用。接口的第一个参数是指向参数的指针,第二参数是函数指针,如果该指针不为空,那么在线程执行完毕退出时,已key指向的内容为入参调用destr_function(),释放分配的缓冲区以及其他数据。
前面已经说了,key是全局变量,不论哪个线程调用了pthread_key_create,所创建的key都是所有的线程都可以访问,每个线程根据自己的需求往key中set不同的值,这就形成了同名而不同值,即同key不同value,一键多值。
在Linux中,TSD池用一个结构体数组来表示,并且PTHREAD_KEYS_MAX默认为1024
cat /usr/include/bits/local_lim.h
static struct pthread_key_struct pthread_keys[PTHREAD_KEYS_MAX] ={{0,NULL}};struct pthread_key_struct
{/* Sequence numbers. Even numbers indicated vacant entries. Notethat zero is even. We use uintptr_t to not require padding on32- and 64-bit machines. On 64-bit machines it helps to avoidwrapping, too. */uintptr_t seq;/* Destructor for the data. */void (*destr) (void *);
};
创建一个TSD,相当于将结构体数组的某一个元素的seq值设置为为“in_use”,并将其索引返回给 *key,然后设置destr_function()为destr()。pthread_key_create()创建一个新的线程私有数据key时,系统会搜索进程中的这个key数组,找出一个未使用的将其索引赋值给 *key。
pthread_key_delete:注销一个键
int pthread_key_delete (pthread_key_t __key);
这个函数不会检查当前是否有线程正在使用TSD,也不会调用清理函数destr_function,而是将TSD对应的seq置un_use,并且将相关线程对应的value置为NULL,以供下一次调用pthread_key_create使用。
pthread_setspecific:为指定key 设置线程私有数据 val
int pthread_setspecific(pthread_key_t key, const void *pointer);
该接口将指针pointer的值(指针值而非其指向的内容)与key相关联,用pthread_setspecific为一个键指定新的线程数据时,线程必须释放原有的数据用以回收空间。
在Linux线程中,使用一个位于 线程描述结构体(_pthread_descr_struct) 中的void **p_specific[PTHREAD_KEY_1STLEVEL_SIZE];
指针数组来存放与key关联的数据val。因为PTHREAD_KEYS_MAX 为1024,所以一维数组大小为32
#define PTHREAD_KEY_2NDLEVEL_SIZE 32
#define PTHREAD_KEY_1STLEVEL_SIZE \
((PTHREAD_KEYS_MAX + PTHREAD_KEY_2NDLEVEL_SIZE -1) \
/ PTHREAD_KEY_2NDLEVEL_SIZE
所以具体存放的位置由key值经过计算得到
idx1st = key/PTHREAD_KEY_2NDLEVEL_SIZE
idx2nd = key%PTHREAD_KEY_2NDLEVEL_SIZE
pthread_getspecific:从指定键读取线程的私有数据
void * pthread_getspecific(pthread_key_t key);
读函数就是将与key关联的数据val读出来,数据类型为void * ,所有可以指向任何类型的数据。通过上面我们得知,数据存在一个32 * 32的二维数组中,所以访问的时候,也是通过计算key值得到数据的位置再返回其内容的
一图诠释底层数据结构的关联
跑个demo看看效果
该demo开了3个线程,第一个线程key对应着一个int的整数,第二个线程key对应着字符串,第三个线程key对应着一个结构体。发现都能正常打印,并且在线程执行完毕退出时,已key指向的内容为入参调用destr_function(),释放分配的缓冲区以及其他数据。
//
// Created by 68725 on 2022/7/29.
//#include<pthread.h>
#include<stdio.h>
#include <malloc.h>
#include <memory.h>#define THREAD_COUNT 3pthread_key_t key;typedef void *(*thread_cb)(void *);void print_thread1_key(void) {int *p = (int *) pthread_getspecific(key);//将值从私有空间中取出来printf("thread 1 : %d\n", *p);
}//线程1 的回调函数
void *thread1_proc(void *arg) {int *p = (int *) malloc(sizeof(int));*p = 68725032;pthread_setspecific(key, p);//将 i传入私有空间中print_thread1_key();
}void print_thread2_key(void) {char *ptr = (char *) pthread_getspecific(key);printf("thread 2 : %s\n", ptr);
}//线程2 的回调函数
void *thread2_proc(void *arg) {char *ptr = (char *) malloc(1024 * sizeof(char));strcpy(ptr, "wxfnb");pthread_setspecific(key, ptr);print_thread2_key();
}struct pair {int x;int y;
};void print_thread3_key(void) {struct pair *p = (struct pair *) pthread_getspecific(key);printf("thread 3 x: %d, y: %d\n", p->x, p->y);
}//线程3 的回调函数
void *thread3_proc(void *arg) {struct pair *p = (struct pair *) malloc(sizeof(struct pair));p->x = 1;p->y = 2;pthread_setspecific(key, p);print_thread3_key();
}void destroy_func(void *val) {printf("free key\n");free(val);
}int main() {pthread_t th_id[THREAD_COUNT] = {0};//3个线程idpthread_key_create(&key, destroy_func);//创建key,这个全局变量可以认为是线程内部的私有空间thread_cb callback[THREAD_COUNT] = {//线程的回调函数thread1_proc,thread2_proc,thread3_proc};int i;for (i = 0; i < THREAD_COUNT; i++) {//创建线程pthread_create(&th_id[i], NULL, callback[i], NULL);}for (i = 0; i < THREAD_COUNT; i++) {pthread_join(th_id[i], NULL);//主线程需要等待子线程执行完成之后再结束}pthread_key_delete(key);
}
Linux线程私有数据Thread-specific Data(TSD) 详解相关推荐
- Linux系统编程——线程私有数据
在多线程程序中.常常要用全局变量来实现多个函数间的数据共享.因为数据空间是共享的,因此全局变量也为全部线程共同拥有. 測试代码例如以下: #include <stdio.h> #inclu ...
- 【Linux系统编程】线程私有数据
00. 目录 文章目录 00. 目录 01. 线程之间共享数据 02. 线程私有数据 2.1 创建线程私有数据 2.2 销毁线程私有数据 2.3 关联线程私有数据成员 2.4 读取线程私有数据所关联的 ...
- 【C/C++多线程编程之十】pthread线程私有数据
多线程编程之线程私有数据 Pthread是 POSIX threads 的简称,是POSIX的线程标准. 线程同步从互斥量[C/C++多线程编程之六]pthread互斥量,信号量[C/C++多线程编 ...
- android字符串获取数字索引,从字符串中提取特定数据(Extract specific data from a string)...
从字符串中提取特定数据(Extract specific data from a string) 我有一个带有描述的长字符串. 我想从字符串中提取一些信息. 但我无法弄明白该怎么做. 这是字符串: C ...
- [免费专栏] Android安全之数据存储与数据安全「详解」
也许每个人出生的时候都以为这世界都是为他一个人而存在的,当他发现自己错的时候,他便开始长大 少走了弯路,也就错过了风景,无论如何,感谢经历 Android安全付费专栏长期更新,本篇最新内容请前往: [ ...
- Linux下的tar归档及解压缩功能详解
Linux下的tar归档及解压缩功能详解 一.Linux下解压缩工具 二.gzip工具的使用方法 三.其他解压缩工具 一.Linux下解压缩工具 二.gzip工具的使用方法 三.其他解压缩工具 一.L ...
- 一文搞定 Spring Data Redis 详解及实战
转载自 一文搞定 Spring Data Redis 详解及实战 SDR - Spring Data Redis的简称. Spring Data Redis提供了从Spring应用程序轻松配置和访问 ...
- linux中流设备_[快速上手Linux设备驱动]之块设备驱动流程详解一
[快速上手Linux设备驱动]之块设备驱动流程详解一 walfred已经在[快速上手Linux设备驱动]之我看字符设备驱动一 文中详细讲解了linux下字符设备驱动,并紧接着用四篇文章描述了Linux ...
- linux下mysql数据库基础及客户端命令详解
linux下mysql数据库基础及客户端命令详解 1.mysql数据库存储引擎: SHOW ENGINES; #查看mysql支持的存储引擎 常见有如下两个存储引擎: MyISAM:每表三个文件: ...
- Linux Kernel 6.0 CXL Core pci.c 详解
文章目录 前言 相关链接 Ref 正文 前言 CXL 是一个比较新的技术,所以我研究的内核源码是选了当前比较新的内核版本 linux 6.0.打算将内核关于 CXL 的驱动进行解析一遍,一步一步慢慢来 ...
最新文章
- 内核电源管理器已启动关机转换_Linux系统启动流程
- UA MATH565C 随机微分方程V Markov Family简介
- jQueryMobile常用技巧
- 流式视频处理架构设计
- 微信小程序页面栈_微信小程序使用页面栈改变上一页面的数据
- ORB_SLAM : semi dense code
- 光线追踪技术 清华大学 pdf_实时光线追踪技术:业界发展近况与未来挑战
- 物联网时代,隐私还有救吗?
- python3 shell脚本开发_python3 subprogress 模块的使用 代替shell编写脚本
- This is why you don’t think you’re creative 你为什么会觉得自己没有创造力?
- 卡巴斯基最新Key下载 生成卡巴斯基Key的工具下载 卡巴斯基Key下载
- 初二生态系统思维导图_有关初中生态系统的思维导图
- Android抓包从未如此简单
- 详解低延时高音质:声音的美化与空间音效篇
- 关于个人目标的一篇博客
- 携程React Native实践
- Python近期练习小案例
- 弱电机房可视化监控综合管理系统设计方案
- 如何将图片放大不改变清晰度?
- 拼音爱好者的好消息:紫光V6发版