linux线程私有数据详解

2023-10-31

    在单线程程序中,函数经常使用全局变量或静态变量,这是不会影响程序的正确性的,但如果线程调用的函数使用全局变量或静态变量,则很可能引起编程错误,因为这些函数使用的全局变量和静态变量无法为不同的线程保存各自的值,而当同一进程内的不同线程几乎同时调用这样的函数时就可能会有问题发生。而解决这一问题的一种方式就是使用线程私有数据。线程私有数据采用了一种被称为一键多值的技术,即一个键对应多个数值。访问数据时都是通过键值来访问,好像是对一个变量进行访问,其实是在访问不同的数据。使用线程私有数据时,首先要为每个线程数据创建一个相关联的键。在各个线程内部,都使用这个公用的键来指代线程数据,但是在不同的线程中,这个键代表的数据是不同的。

 根据<<unix环境高级编程>>中所涉及的设计线程私有数据的原因:

(1).在维护每个线程的私有数据的时候,我们可能会想到分配一个保存线程数据的数组,用线程的ID 作为数组的索引来实现访问,但是有一个问题是系统生成的线程ID不能保证是一个小而连续的整数, 并且用数组实现的时候由于其他线程也可以访问其数组中的数据,这样会 引起数据混乱。
(2).它提供了让基于进程的接口适应多线程环境的机制.一个很明显的实例就是errno。以前的接口(线程 出现以前)把errno定义为进程上下文中全局可访问的整数。系统调用和库例程在调用或执行失败时设置 errno,把它作为操作失败的附属结果。为了让线程也能够使用那些原本基于进程的系统调用和库例程,errno 被重定义为线程私有数据。这样,一个线程做了重置errno的操作也不会影响进程中其他线程的errno值。

线程私有数据的几个重要的含有:

1.int pthread_key_create (pthread_key_t *key, void (*destructor)(void *));
//第一个参数为指向一个键值的指针,第二个参数指明了一个destructor函数,如果这个参数不为空,那么当每个线程结束时,系统将调用这个函数来释放绑定在这个
//键上的内存块。
2.int pthread_key_delete (pthread_key_t key);
3.int pthread_setspecific (pthread_key_t key, const void *value);
//其中value就是不同的线程中,key值所关联的私有数据地址,这些地址可以用malloc来分配;
4.void *pthread_getspecific (pthread_key_t key);
//获取线程私有数据的地址
5.int phread_once(pthread_once_t *onceptr, vid(*init)(void));
注意:pthread_once 使用onceptr 参数指向的变量中的值确保init参数所指的函数在进程范围内之被调用一次,
onceptr必须是一个非本地变量(即全局变量或者静态变量),而且必须初始化为PTHREAD_ONCE_INIT。
pthread_key_t key;
pthread_once_t once = PTHREAD_ONCE_INIT;
void destructor(void *ptr){
     free(ptr);
}
void excute_once(void)  {
    pthread_key_create(&key, destructor);

}
int main(){
   pthread_once(&r1_once, excute_once);
}

当我们调用pthread_key_create时,内核从Linux的TSD池中分配一项,将其值赋给key供以后访问使用,它的第一个参数key为指向键值的指针,第二个参数为一个函数指针,如果指针不为空,则在线程退出时将以key所关联的数据为参数调用destr_function(),释放分配的缓冲区。 key一旦被创建,所有线程都可以访问它,但各线程可以根据自己的需要往key中填入不同的值,这就相当于提供了一个同名而不同值的全局变量,一键多值。其中TSD池的结构如下:

内核支持的pthread_keys是有限的(可能是128也可能),除了进程范围内的Key结构数组之外,系统还在进程内维护了关于多个线程的多条信息。这些特定于线程的信息我们称之为Pthread结构。其中部分内容是我们称之为pkey数组的一个128个元素的指针数组。系统维护的关于每个线程的信息结构图如下:


一旦我们在某个线程里面调用pthread_setspecific将key与某个空间(这个空间可以是malloc申请的)相关连的时候,上面的结构就变成

下面我们用一个例子来解释线程的私有结构:

#include <limits.h>  
#include <string.h>  
#include <pthread.h>  
#include <stdlib.h>  
#include <stdio.h>
#include<iostream>
using namespace std;
pthread_key_t key;
pthread_key_t key1;
pthread_key_t key2;
void func1(){
	int *tmp = (int*)pthread_getspecific(key);
}

void *tthread_fun(void* args){
	pthread_setspecific(key,args);
	int *tmp = (int*)pthread_getspecific(key);
	printf("线程%lu的私有地址为:%p\n",pthread_self(),tmp);
	*tmp+=1;
	func1();
	return (void*)0;		
}

int main(){
	pthread_t pa, pb;
	pthread_key_create(&key,NULL);
	pthread_key_create(&key1,NULL);
	pthread_key_create(&key2,NULL);
	cout<<key<<" "<<key1<<" "<<key2<<endl;
	pthread_t pid[3];
	int a[3]={100,200,300};
	cout<<"the address of array is: "<<&a<<endl;
	int i=0;
	for(i=0;i<3;i++){
		pthread_create(&pid[i],NULL,tthread_fun,&a[i]);
		cout<<"pthread id is: "<<pid[i]<<endl;

	}
	for(i=0;i<3;i++){
		pthread_join(pid[i],NULL);

	}
	cout<<a[0]<<" "<<a[1]<<" "<<a[2]<<endl;
	return 0;
}
输出结果:

从测试结果我们可以看出

(1):key的值是从0(key)开始分配的,然后是1(key1),再然后是2(key2);

(2):在进程里我们一共创建了3个进程,这三个进程共同使用一个键key(它额值为0),那么对应于每一个线程中的pkey[0]不同的线程的指向不同,就上面的程序而言,pid[0](可以看成线程A)的pkey[0]里存放的是变量a[0]地址,pid[1](可以看成线程B)的pkey[0]里存放的是变量a[1]地址,pid[2](可以看成线程C)的pkey[0]里存放的是变量a[2]地址,这三个变量分别作为这三个线程的私有数据;函数fun1的关联结构为:


(3):在不同的线程中:

int *tmp = (int*)pthread_getspecific(key);
虽然同样是key,但是我们得到的tem的地址对于每个线程来说不同,这些地址也就是图3中pkey[0]所指的地址;


另外还做了另一个实验:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <pthread.h>
#define THREAD_NUM 3

void * thread_handler(void *arg){
        printf("process id: %d\tthread id: %lu\n", getpid(),pthread_self());
}
int main(){
        int i;
        pthread_t tid[THREAD_NUM];
	printf("process id: %d\tthread id: %lu\n", getpid(),pthread_self());
        for(i = 0; i < THREAD_NUM; i++){
                pthread_create(&tid[i], NULL, thread_handler, NULL);
        }
        for(i = 0; i < THREAD_NUM; i++){
                pthread_join(tid[i],NULL);
        }
	return 0;
}
输出结果:


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <pthread.h>
#define THREAD_NUM 3
int main(){
        int i;
	printf("process id: %d\tthread id: %lu\n", getpid(),pthread_self());
	return 0;
}

输出结果:

从上面两个例子可以看出,进程可以看成是主线程,占用一个线程号!!!!!

相关文章:

http://www.cnblogs.com/snake-hand/p/3148191.html
http://blog.csdn.net/caigen1988/article/details/7901248
http://www.2cto.com/kf/201312/261554.html
http://blog.csdn.net/cywosp/article/details/26469435

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux线程私有数据详解 的相关文章

随机推荐

  • 基于Golang的gRPC框架使用与开发

    一 gRPC 基础 包括 rpc gRPC Protobuf 等概念 网上已经有详细的介绍 此处不再过多说明 关于 gRPC 的介绍 可以参考官方介绍文档 以及微软官方文档介绍 通俗的来说 gRPC 是一种 rpc 的具体实现 其利用 Pr
  • 五、STL容器:STL算法总结

    5 STL算法 5 1 构成 头文件 功能
  • 从被吐槽的Amazon看,如何建设好的on call机制?

    对于每个程序员来说 进入市值万亿的Amazon工作 似乎都是一件值得骄傲的事 但只要在网络上一提起Amazon 关于它的on call 机制的吐槽就会此起彼伏 要知道在互联网公司 on call应该是非常普遍的现象 但大家为什么对Amazo
  • 职工管理系统(超详细版c++)

    一 项目概述 编写一个有添加 删除 修改 显示 排序 查找功能的职工管理系统 对一些职工的信息进行处理 保存到文本文件中 便于后续使用 二 创建项目 在本篇文章中我们使用vs2022版本编写程序 为了使我们的代码结构严谨 减少冗余 便于修改
  • 我从未结束的Java之旅(二)

    目录 大胆北漂 餐饮 团队扩张 扩张的烦恼 团队管理的探索 大胆北漂 餐饮 团队扩张 由于公司业务的扩展以及战略方向的变更 之前负责得小团队不得不扩招 由5个人得团队补充到了20人 当时我们saas服务已经很庞大了 基本涉及到餐饮相关的所有
  • Go语言之JSON解码函数Unmarshal

    直接上代码 package main import encoding json fmt 定义Actress结构体 type Actress struct Name string Birthday string BirthPlace stri
  • springboot 之 JPA

    一 说明 JPA提供了操作数据库的接口 在开发过程中继承和使用这些接口 可简化现有的持久化开发的工作 二 JPA提供的接口 1 JpaRepository JpaRepository继承自PagingAndSortingRepository
  • P1541 [NOIP2010 提高组] 乌龟棋(dp)dp5

    https www luogu com cn problem P1541 include
  • 【MySQL】表的内外连接

    需要云服务器等云产品来学习Linux的同学可以移步 gt 腾讯云 lt gt 阿里云 lt gt 华为云 lt 官网 轻量型云服务器低至112元 年 新用户首次下单享超低折扣 目录 一 内连接 表1 inner join 表2 on 1 显
  • DBNN实验进展

    实验环境 cpu version 1 Precsion Recall f1 scorem precision of mbnn is 0 8756 recall of mbnn is 0 8670 f1 score of mbnn is 0
  • 元宇宙(四)ROBLOX

    至2019年 已有超过500万的青少年开发者使用Roblox开发3D VR等数字内容 吸引的月活跃玩家超1亿 Roblox在玩法上进一步创新升级 类型更全面 除了传统的探索建造类沙盒玩法外 Roblox还覆盖主流的角色扮演 第一人称射击 动
  • 第9届Python编程挑战赛北京赛区复赛真题剖析-2023年全国青少年信息素养大赛

    导读 超平老师计划推出 全国青少年信息素养大赛Python编程真题解析 50讲 这是超平老师解读Python编程挑战赛系列的第16讲 全国青少年信息素养大赛 原全国青少年电子信息智能创新大赛 是 世界机器人大会青少年机器人设计与信息素养大赛
  • 不同格式点云存储结构(txt、pcd、las、ply)整理以及基本的读写、可视化方法

    不同格式点云存储结构整理以及基本的读写 可视化方法 一 文本 txt 1 1 存储结构 1 2 读取 二 PCD格式 1 1 存储结构 1 2 读写 1 2 1 open3d读写 python 1 2 2 PCL读写 C 三 LAS格式 3
  • 基于马尔可夫链的写作机器人软件

    数据结构课设 二 作业要求 设计并实现一个基于马尔可夫链的写作机器人软件 软件通过对素材文本的学习 建立词库以及单词的马尔可夫链 然后使用所建立的单词马尔可夫链配合随机数选择 自动生成一段文字 分析 由于不太清楚老师的具体要求 所以在网上找
  • 微信小程序如何根据不同用户切换不同TabBar

    现有需求 小程序用户有三种身份 公众 运维人员 领导 根据不同用户身份显示不同的tabbar 众所周知微信小程序全局文件app json里面的 tabBar 里面的list只能放置2 5个 要想实现3个tabbar 必须得复用tabbar
  • 回归分析详解及matlab实现

    当人们对研究对象的内在特性和各因素间的关系有比较充分的认识时 一般用机理分析方法建立数学模型 如果由于客观事物内部规律的复杂性及人们认识程度的限制 无法分析实际对象内在的因果关系 建立合乎机理规律的数学模型 那么通常的办法是搜集大量数据 基
  • 惊!STM32 蓝牙串口模块(H21/JDY-31) 竟如此简单!

    惊 STM32 蓝牙串口模块 H21 JDY 31 竟如此简单 文章日志 1 写于2022 08 19 文章目录 1 认识蓝牙串口模块 2 困扰我很久的实验竟如此简单 3 一些现象的思考 1 认识蓝牙串口模块 JDY 31 蓝牙基于蓝牙 3
  • sql服务器显示空白,sql服务器空白

    sql服务器空白 内容精选 换一换 登录Windows操作系统的裸金属服务器时 需使用密码方式登录 因此 用户需先根据创建裸金属服务器时使用的密钥文件 获取该裸金属服务器初始安装时系统生成的管理员密码 Administrator帐户或Clo
  • 剑指 Offer 50. 第一个只出现一次的字符

    剑指 Offer 50 第一个只出现一次的字符 题目 题目链接 具体代码 集合缓存法 索引比较法 题目 题目链接 https leetcode cn com problems di yi ge zhi chu xian yi ci de z
  • linux线程私有数据详解

    在单线程程序中 函数经常使用全局变量或静态变量 这是不会影响程序的正确性的 但如果线程调用的函数使用全局变量或静态变量 则很可能引起编程错误 因为这些函数使用的全局变量和静态变量无法为不同的线程保存各自的值 而当同一进程内的不同线程几乎同时