详解Linux线程私有数据
csdn 2023-12-05

线程私有数据

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

而解决这一问题的一种方式就是使用线程私有数据。线程私有数据采用了一种被称为一键多值的技术,即一个键对应多个数值。访问数据时都是通过键值来访问,好像是对一个变量进行访问,其实是在访问不同的数据。使用线程私有数据时,首先要为每个线程数据创建一个相关联的键。在各个线程内部,都使用这个公用的键来指代线程数据,但是在不同的线程中,这个键代表的数据是不同的。

这和JAVA中的ThreadLocal变量的思想有点像:尽管是一个变量,但是在不同的线程中调用就是取出的不同的值。

设计线程私有数据的原因:

  • 在维护每个线程的私有数据的时候,我们可能会想到分配一个保存线程数据的数组,用线程的ID作为数组的索引来实现访问,但是有一个问题是:系统生成的线程ID不能保证是一个小而连续的整数, 并且用数组实现的时候由于其他线程也可以访问其数组中的数据,这样会引起数据的不安全;
  • 线程私有数据提供了让基于进程的接口适应多线程环境的机制。

一个很明显的实例就是errno,以前的接口(线程出现以前)把errno定义为进程上下文中全局可访问的整数。系统调用和库例程在调用或执行失败时设置 errno,把它作为操作失败的附属结果。为了让线程也能够使用那些原本基于进程的系统调用和库例程,errno 被重定义为线程私有数据。这样,一个线程做了重置errno的操作也不会影响进程中其他线程的errno值。


线程私有变量函数

线程私有变量的相关函数为:

线程私有变量的初始化

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

函数说明:key参数为指向一个键值的指针,第二个参数指明了一个destructor函数,如果这个参数不为空,那么当每个线程结束时,系统将调用这个函数来释放绑定在这个键上的内存块。

销毁线程私有变量

int pthread_key_delete (pthread_key_t key);

函数说明:key参数为一个键值。

向线程私有变量赋值

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

函数说明:value参数是不同的线程中,key值所关联的私有数据地址,这些地址可以用malloc来分配。

获取线程私有变量

void *pthread_getspecific (pthread_key_t key);

函数说明:key参数为一个键值,返回值是线程私有变量的数据地址。

指定函数只执行一次

int phread_once(pthread_once_t *onceptr, void(*init)(void));

函数说明:本函数使用初值为PTHREAD_ONCE_INIT的once_control变量保证init_routine()函数在本进程执行序列中仅执行一次。注意的是,onceptr必须是一个非本地变量(即全局变量或者静态变量),而且必须初始化为PTHREAD_ONCE_INIT。

一般使用环境:在多线程环境中,有些事仅需要执行一次。通常当初始化应用程序时,可以比较容易地将其放在main函数中。但当你写一个库时,就不能在main里面初始化了,你可以用静态初始化,但使用一次初始化(pthread_once)会比较容易些。

例子:

pthread_key_t key;
pthread_once_t r1_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);
}


TSD池

作为TSD池,就是线程私有数据(Thread-specific Data)池。

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

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

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


实际例子

#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
 
pthread_key_t key;
 
void echomsg( void* param )
{
        printf( "destructor excuted in thread %lu, param = %lu\n", pthread_self(), *((unsigned long int*)param) );
}
 
void* child1( void* param )
{
        unsigned long int  tid = pthread_self();
        printf( "thread %lu enter\n", tid );
        pthread_setspecific( key, ( void* )tid );
 
        sleep( 2 );
        unsigned long int  val = *((unsigned long int *)pthread_getspecific( key ));
        printf( "thread %lu returns %lu\n", tid, val );
        sleep( 5 );
 
        return ( void* )0;
}
 
 
void* child2( void* param )
{
        unsigned long int tid = pthread_self();
        printf( "thread %lu enter\n", tid );
        pthread_setspecific( key, ( void* )tid );
 
        sleep( 1 );
        unsigned long int  val = *( (unsigned long int*)pthread_getspecific( key ) );
        printf( "thread %lu returns %lu\n", tid, val );
        sleep( 5 );
 
        return ( void* )0;
}
 
int main()
{
        pthread_t tid1, tid2;
        printf( "main thread enter\n" );
 
        pthread_key_create( &key, echomsg );
        pthread_create( &tid1, NULL, child1, NULL );
        pthread_create( &tid2, NULL, child2, NULL );
 
        sleep( 10 );
        pthread_key_delete( key );
        printf( "main thread exit\n" );
 
        return 0;
}

通过g++编译命令:

g++ -lpthread -o TSD.out TSD.cpp

最终的运行结果为:

kennie@cbib:~/pthreadDIR$ ./TSD.out
main thread enter
thread 3075607408 enter
thread 3067214704 enter
thread 3067214704 returns 3067214704
thread 3075607408 returns 3075607408
destructor excuted in thread 3067214704, param = 3067214704
destructor excuted in thread 3075607408, param = 3075607408
main thread exit


声明: 本文转载自其它媒体或授权刊载,目的在于信息传递,并不代表本站赞同其观点和对其真实性负责,如有新闻稿件和图片作品的内容、版权以及其它问题的,请联系我们及时删除。(联系我们,邮箱:evan.li@aspencore.com )
0
评论
  • 【7.24 深圳】2025国际AI+IoT生态发展大会/2025全球 MCU及嵌入式技术论坛


  • 相关技术文库
  • 单片机
  • 嵌入式
  • MCU
  • STM
  • 3AT89C51单片机引脚说明及引脚图

    AT89C51是一种带4K字节闪烁可编程可擦除只读存储器的低电压,高性能CMOS8位微处理器,俗称单片机。该器件采用ATMEL高密度非易失存储器制造技术制造,与工业标准的MCS-51指令集和输出管脚相兼容。由于将多功能8位CPU...

    07-11
  • 51单片机对LCD1602液晶的驱动设计

    51单片机——LCD1602 1、1602液晶读写时序 (1)、读状态 RS=L,R/W=H,E=H。(判断忙完毕后释放总线) (2)、读数据 RS=H,R/W=H,E=H。 (3)、写指令 RS=L,R/W=L,D0~D7=指令码,E=高脉冲 (4)、写数据 RS=H,R/W=L,D0~D...

    07-11
  • 单片机串口如何接收不定长数据的?

    我们在使用其他STM32的单片机的时候,会发现有些困难,会发现常用的方法并不能用,在还没有接收完数据的时候,就解决不了。于是,只能用通用的方法来解决了。 这个通用的方法,其实原理和使用IDLE的原理一样:...

    07-11
  • ARM处理器的选型原则

    鉴于ARM微处理器的众多优点,随着国内外嵌入式应用领域的逐步发展,ARM微处理器必然会获得广泛的重视和应用。但是,由于ARM微处理器有多达十几种的内核结构,几十个芯片生产厂家,以及千变万化的内部功能配置组合,...

    07-10
  • 有哪些低功耗设计方法?单片机系统低功耗设计要点介绍

    功耗,已经是一个老生常谈的话题了。对于功耗,大家多多少少有所了解。目前,很多产品的宣传里便带有低功耗噱头。为增进大家对功耗的认识,本文将基于两点介绍功耗:1.低功耗主要设计方法,2.单片机系统低功耗设计...

    07-10
  • 8位32位MCU如何选择?如何选择合适的MCU?

    MCU,对于普通人而言,是一个高大上的存在。但是,在工业中,MCU确实常见产品。为增进大家对MCU的认识,本文将基于两点介绍MCU:1.8位MCU和32位MCU如何选择?2.如何选择合适的MCU。如果你对MCU具有兴趣,不妨继续往...

    07-09
  • ARM开发:一 ARM微处理器概述

    1.1ARM-Advanced RISC Machines ARM(Advanced RISC Machines),既可以认为是一个公司的名字,也可以认为是对一类微处理器的通称,还可以认为是一种技术的名字。 1991年ARM公司成立于英国剑桥,主要出售芯片设计技术...

    07-08
  • 分析C51单片机的一些误区和注意事项

    简介:常看见初学者要求使用_at_,这是一种谬误,把C当作ASM看待了。在C中变量的定位是编译器的事情,初学者只要定义变量和变量的作 用域,编译器就把一个固定地址给这个变量。怎么取得这个变量的地址?要用指针。 1) C...

    07-08
  • 51单片机几个延时程序

    简介:51单片机几个精确延时程序:在精确延时的计算当中,最容易让人忽略的是计算循环外的那部分延时,在对时间要求不高的场合,这部分对程序不会造成影响. 一. 500ms延时子程序(晶振12MHz,一个机器周期1us.) 程...

    07-08
  • 总结单片机软件抗干扰的几种办法

    简介:在提高硬件系统抗干扰能力的同时,软件抗干扰以其设计灵活、节省硬件资源、可靠性好越来越受到重视。下面以MCS-51单片机系统为例,对微机系统软件抗干扰方法进行研究。 1、软件抗干扰方法的研究 在工程实践中...

    07-08
  • 基于C51单片机实现汽车座椅自动控制系统的软硬件设计

    引言 随着人们生活水平的提高,对汽车座椅的舒适性要求也越来越高,要求对汽车座椅地调节能够更加简单、方便、快捷。目前,汽车座椅位置的调节多采用基于手动调节方式的机械和电动控制两种方式。汽车座椅位置的调节...

    07-02
  • MCS51单片机程序设计时堆栈的计算方法解析

    用C语言进行MCS51系列单片机程序设计是单片机开发和应用的必然趋势。Keil公司的C51编译器支持经典8051和8051派生产品的版本,通称为Cx51。应该说,Cx51是C语言在MCS51单片机上的扩展,既有C语言的共性,又有它自己...

    07-02
下载排行榜
更多
评测报告
更多
广告