pthread_getspecific和pthread_setspecific详解

简介: pthread_getspecific和pthread_setspecific详解

写在前面

在Linux系统中使用C/C++进行多线程编程时,我们遇到最多的就是对同一变量的多线程读写问题,大多情况下遇到这类问题都是通过锁机制来处理,但这对程序的性能带来了很大的影响,当然对于那些系统原生支持原子操作的数据类型来说,我们可以使用原子操作来处理,这能对程序的性能会得到一定的提高。那么对于那些系统不支持原子操作的自定义数据类型,在不使用锁的情况下如何做到线程安全呢?本文将从线程局部存储方面,简单讲解处理这一类线程安全问题的方法。

一、一次性初始化

在讲解线程特有数据之前,先让我们来了解一下一次性初始化。多线程程序有时有这样的需求:不管创建多少个线程,有些数据的初始化只能发生一次。在多线程的情况下,为了能让该对象能够安全的初始化,一次性初始化机制就显得尤为重要了。——在设计模式中这种实现常常被称之为单例模式。Linux中提供了如下函数来实现一次性初始化:

函数原型:

int pthread_once (pthread_once_t *once_control, void (*init) (void));

函数pthread_once()可以确保无论有多少个线程调用多少次该函数,也只会执行一次由init所指向的由调用者定义的函数。init所指向的函数没有任何参数,形式如下:

void init(void)

##二、线程局部数据API

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

函数pthread_key_create()为线程局部数据创建一个新键,并通过key指向新创建的键缓冲区。destructor所指向的是一个自定义的函数,其格式如下:

void destructor(void *value){}

只要线程终止时与key关联的值不为NULL,则destructor所指的函数将会自动被调用。如果一个线程中有多个线程局部存储变量,那么对各个变量所对应的destructor函数的调用顺序是不确定的,因此,每个变量的destructor函数的设计应该相互独立。

2.2 pthread_setspecific

函数原型:

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

用于将value的副本存储于一数据结构中,并将其与调用线程以及key相关联。参数value通常指向由调用者分配的一块内存,当线程终止时,会将该指针作为参数传递给与key相关联的destructor函数。当线程被创建时,会将所有的线程局部存储变量初始化为NULL,因此第一次使用此类变量前必须先调用pthread_getspecific()函数来确认是否已经于对应的key相关联,如果没有,那么pthread_getspecific()会分配一块内存并通过pthread_setspecific()函数保存指向该内存块的指针。

2.3 pthread_getspecific

函数原型:

void *pthread_getspecific (pthread_key_t key);

将pthread_setspecific()设置的value取出。在使用取出的值前最好是将void*转换成原始数据类型的指针。

三、深入理解线程局部存储机制

3.1 一个全局(进程级别)的数组,用于存放线程局部存储的键值信息

pthread_key_create()返回的pthread_key_t类型值只是对全局数组的索引,该全局数组标记为pthread_keys,其格式大概如下:

数组的每个元素都是一个包含两个字段的结构,第一个字段标记该数组元素是否在用,第二个字段用于存放针对此键、线程局部存储变的解构函数的一个副本,即destructor函数。

每个线程还包含一个数组,存有为每个线程分配的线程特有数据块的指针(通过调用pthread_setspecific()函数来存储的指针,即参数中的value)


推荐一个零声学院免费教程,个人觉得老师讲得不错,分享给大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,点击立即学习:

目录
打赏
0
0
0
0
3
分享
相关文章
正则表达式匹配域名、网址、url
DNS规定,域名中的标号都由英文字母和数字组成,每一个标号不超过63个字符,也不区分大小写字母。标号中除连字符(-)外不能使用其他的标点符号。级别最低的域名写在最左边,而级别最高的域名写在最右边。由多个标号组成的完整域名总共不超过255个字符。
30733 0
CentOS 7- 配置阿里镜像源
阿里镜像官方地址http://mirrors.aliyun.com/ 1、点击官方提供的相应系统的帮助 :2、查看不同版本的系统操作: 下载源1、安装wget yum install -y wget2、下载CentOS 7的repo文件wget -O /etc/yum.
246105 0
【Linux C/C++ 线程同步 】Linux互斥锁和条件变量:互斥锁和条件变量在Linux线程同步中的编程实践
【Linux C/C++ 线程同步 】Linux互斥锁和条件变量:互斥锁和条件变量在Linux线程同步中的编程实践
357 0
HTTPS:保护你的网站免受中间人攻击的关键技术
【6月更文挑战第13天】HTTPS是抵御中间人攻击的关键技术,通过数据加密、身份验证和完整性保护保障网络安全。它基于SSL/TLS协议加密通信,防止数据被窃取或篡改,并使用数字证书确认服务器身份,避免伪造。要确保HTTPS安全,需使用有效数字证书,启用强制HTTPS,定期更新维护并限制访问范围。
简单易操作 VsCoe离线安装插件【步骤+图片+插件】
这篇文章介绍了在Visual Studio Code (VSCode) 中进行离线安装插件的详细步骤,包括如何下载插件、以SVN插件为例的离线安装过程、通过命令行安装以及一个更加简单的离线安装方式,还提供了操作界面的截图帮助理解。
简单易操作 VsCoe离线安装插件【步骤+图片+插件】
条件变量函数pthread_cond_timedwait实现业务场景
条件变量函数pthread_cond_timedwait实现业务场景
308 0
Docker从入门到精通:Docker pull命令学习
了解Docker镜像下载方法!使用`docker pull`命令从[Docker Hub](https://hub.docker.com/)获取镜像。基本语法是`docker pull NAME[:TAG]`。例如,拉取Python最新镜像的命令是`docker pull python`或`docker pull python:latest`。可选参数包括`-a`(拉取所有标签)和`--quiet`(只显示进度条)。拉取后,用`docker images`检查镜像是否成功存储。开始你的容器化之旅吧!
MQTT常见问题之mqtt第4—6次调用会报错如何解决
MQTT(Message Queuing Telemetry Transport)是一个轻量级的、基于发布/订阅模式的消息协议,广泛用于物联网(IoT)中设备间的通信。以下是MQTT使用过程中可能遇到的一些常见问题及其答案的汇总:
《人生苦短,我用python·二》python在C++中的断点调试
《人生苦短,我用python·二》python在C++中的断点调试
202 0
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问