linux线程特有数据(key)与线程局部存储(__thread)

线程特有数据(key)与线程局部存储(__thread)

要了解线程特有数据局部存储的特性。首先需要了解什么是线程安全函数,什么是非线程安全函数

  1. 线程安全函数:函数可同时供多个线程安全调用.即可重入,函数重入时不会造成函数的逻辑混乱。
  2. 非线程安全函数:如果不是线程安全函数,那么他就是非线程安全函数。导致非安全的主要原因是函数使用了全局变量,内存分配等,在多线程并行访问此函数时,会造成此函数的逻辑错误。

将一个非线程安全函数转换为线程安全函数的方法很多,如:

  1. 使用互斥量。对函数中的共享资源进行保护,保证函数逻辑的正确性。
    1. 缺点:在线程遇到互斥量时,并行访问就转换成了顺序访问。
  2. 避免使用全局和静态变量等会造成函数不可重入的机制。
  3. 使用线程特有数据与局部存储。一般用于库函数的编写中。

一般库函数的编写中,可能会用到线程特有数据与局部存储。

线程特有数据

线程特有数据:简单的说,就是为每个调用线程分别维护一份变量的副本。每个线程通过特有数据键访问时,这个特有数据键都会获取到本线程绑定的变量副本。

线程特有数据的使用:

  1. 首先通过pthread_key_create()函数创建初始化一个特有数据键,并绑定解构函数(线程在中止时调用,一般用于内存释放等)。
  2. 然后通过pthread_getspecific()绑定键与变量实体。
  3. 最后通过pthread_getspecific()函数获取变量实体并访问。

通过特有数据键来获取与访问。特有数据键是全局的。

  1. int pthread_key_create(pthread_key_t *key, void (*destructor)(void *))
    1. 创建一个特有数据键。用于之后绑定特有数据。
    2. key:特有数据键实体指针。
    3. destructor:解构函数。线程终止时调用。
    4. return:
  2. int pthread_setspecific(pthread_key_t key, const void *value)
    1. 绑定特有数据键与数据实体
    2. key:特有数据键
    3. value:数据内容实体。
  3. void *pthread_getspecific(pthread_key_t key)
    1. 获取指定键对应的数据内容。如果未绑定数据实体。则返回NULL。可以利用这一点来判断自身是否是初次为某个线程所调用,若为初次,
      则必须为该线程分配空间。
    2. 返回数据实体指针。如果未绑定数据实体。则返回NULL。

众所周知,strerror()是一个非线程安全函数。下面的例程展示了使用特有数据编写strerror()函数,使其成为线程安全函数.例程如下。

#define MAX_ERROR_LEN 256

static pthread_once_t once = PTHREAD_ONCE_INIT;
static pthread_key_t strerrorKey;

/* 线程终止时会调用此函数进行内存释放 */
static void destructor(void *buf)
{
    free(buf);
}

static void createKey(void)
{
    /* 创建特有数据键 */
    int s = pthread_key_create(&strerrorKey, destructor);
    if (s != 0){
        printf("key_create error.\n");
        exit(1);
    }
}

void my_strerror(int err)
{
    int s;
    char *buf;

    /* 1. 只初始化一次->创建特有数据键 */
    pthread_once(&once, createKey);

    /* 2. 查看特有数据键对应的内存是否为空 */
    buf = pthread_getspecific(strerrorKey);
    if (buf == NULL) { //为空则分配内存
        buf = malloc(MAX_ERROR_LEN);
        if (buf == NULL){
            exit(1);
        }

        /* 3. 给特有数据键绑定内存 */
        if (pthread_setspecific(strerrorKey, buf) != 0){
            printf("setspecific error.\n");
        }
    }

    if (err < 0 || err >= _sys_nerr || _sys_errlist[err] == NULL) {
        snprintf(buf, MAX_ERROR_LEN, "Unknown error %d", err);
    } else {
        strncpy(buf, _sys_errlist[err], MAX_ERROR_LEN - 1);
        buf[MAX_ERROR_LEN - 1] = '\0'; /* Ensure null termination */
    }

    return buf;
}

线程局部存储

正常情况下我们用static定义的变量,会被存放到进程的初始化数据区,这导致各个线程都共享这个变量。而线程局部存储在定义变量时,使用__thread修饰变量.此时,每个线程都会拥有一份对变量的拷贝。线程局部存储中的变量将一直存在,直至线程终止,届时会自动释放这一存储。

局部存储的使用更简单,只需要在定义变量时,使用__thread修饰。如static __thread buf[MAX_ERROR_LEN];.

使用线程局部存储实现strerror()安全版更简单。如下:

#define MAX_ERROR_LEN 256
static __thread char buf[MAX_ERROR_LEN];

void my_strerror(int err)
{
    if (err < 0 || err >= _sys_nerr || _sys_errlist[err] == NULL) {
        snprintf(buf, MAX_ERROR_LEN, "Unknown error %d", err);
    } else {
        strncpy(buf, _sys_errlist[err], MAX_ERROR_LEN - 1);
        buf[MAX_ERROR_LEN - 1] = '\0'; /* Ensure null termination */
    }

    return buf;
}

关于技术交流

此处后的文字已经和题目内容无关,可以不看。
qq群:825695030
微信公众号:嵌入式的日常
如果上面的文章对你有用,欢迎打赏、点赞、评论。二维码

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

theboynoName

感谢鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值