Linux线程局部变量实现

2021-01-06  本文已影响0人  突击手平头哥

Linux线程局部变量实现

什么是线程局部变量,就是每个线程各自拥有一个的变量;比如errno,是每个线程各自拥有的全局变量;在代码实践当中来说,这种技术还是很有用的。C语言中实际上由很多强大的函数,比如:__sync函数族、pthread_once函数,C++中的原子函数,GCC就已经帮我们实现了;当然这些函数很多是依赖于GCC编译器或者某个特定库的,需要注意。

错误码errno的实现

errnoLinux中用来表述错误的变量,int类型,是一个全局变量,但是在使用当中我们却不需要考虑多线程的问题;errno算不上很好的设计,但是也难找到更好的了

查看/usr/include/errno.h中的定义:

/* The error code set by various library functions.  */
extern int *__errno_location (void) __THROW __attribute_const__;
# define errno (*__errno_location ())

  到这里可以看到实际上errno是一个函数的返回值,并且是指针解引用获得的;从这里笔者不能看出什么,但是也没能找到源码;就不做深究,因为接下来笔者会介绍一些局部变量的实现原理以及实现方式

pthread_once函数

单例模式的实现方式由很多种,但是当遭遇到多线程时就较为复杂,如果在main函数中可以统一初始化,但是当我们写的时动态库时就不太方便;pthread_once就可以保证其指定的函数在多个线程中仅执行一次

extern int pthread_once (pthread_once_t *__once_control,
                         void (*__init_routine) (void)) __nonnull ((1, 2));
#include <iostream>
#include <pthread.h>
#include <thread>

void debug_pthread_once()
{
    std::cout << "pthread once" << std::endl;
}

int main() {

    pthread_once_t once = 0;
    std::thread th1([&once](){
        for (int i = 0; i < 10; i++)
            pthread_once(&once, debug_pthread_once);
    });
    
    std::thread th2([&once](){
        for (int i = 0; i < 10; i++)
            pthread_once(&once, debug_pthread_once);
    });
    th1.join();
    th2.join();

    return 0;
}
pthread once

  测试可以验证,pthread_once确实仅仅执行了一次

参数说明

  pthread_once_t这个参数定义为int有三种状态:NEVER(0)IN_PROGRESS(1)DONE(2);如果once被设置为2就不会被执行,为1则会阻塞。从这段代码来说,以基础库也可以实现类似的效果,但是需要加锁。

pthread_key_create函数

该函数可以辅助实现类似于errno这样的线程全局变量,在使用时和全局变量没有区别,但是每个线程独有

函数原型

#include <pthread.h>
int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
int pthread_setspecific(pthread_key_t key,const void *pointer));
void *pthread_getspecific(pthread_key_t key);
int pthread_key_delete(pthread_key_t key);

使用方法

例子来源于man说明

static pthread_key_t key;                               //全局变量
static pthread_once_t key_once = PTHREAD_ONCE_INIT;     //pthread_once

static void
make_key()
{
    (void) pthread_key_create(&key, NULL);
}

func()
{
    void *ptr;

    (void) pthread_once(&key_once, make_key);           //多个线程,但是只需要创建一个key 
    if ((ptr = pthread_getspecific(key)) == NULL) {     //获取线程独享数据
        ptr = malloc(OBJECT_SIZE);
        ...
        (void) pthread_setspecific(key, ptr);           //取出线程独享数据
    }
    ...
}

errno的代码实现

使用以上函数实现一个类似于errno的功能

#include <iostream>
#include <pthread.h>
#include <thread>
#include <unistd.h>

static pthread_key_t key;                               //全局变量
static pthread_once_t key_once = PTHREAD_ONCE_INIT;     //pthread_once

void key_exit(void *ptr)
{
    printf("errno: %u %d\n", ptr, *(int*)ptr);
    free(ptr);
}

static void make_key()
{
    (void) pthread_key_create(&key, key_exit);
}

int *__errno_location(void)
{
    pthread_once(&key_once, make_key);
    void *ptr = pthread_getspecific(key);
    if (!ptr) {
        ptr = malloc(sizeof(int));
        pthread_setspecific(key, ptr);
    }
    return (int*)ptr;
}

int main() {
    std::thread th1([](){
        for (int i = 1; i < 5; i++) {
            *__errno_location() = i;
            usleep(20000);
        }


    });

    std::thread th2([](){
        for (int i = 5; i < 10; i++) {
            *__errno_location() = i;
            usleep(20000);
        }
    });

    std::thread th3([](){
        for (int i = 11; i < 15; i++) {
            *__errno_location() = i;
            usleep(20000);
        }
    });


    th1.join();
    th2.join();
    th3.join();

    return 0;
}
errno: 3019901728 14
errno: 2952792864 4
errno: 2885684000 9
上一篇下一篇

猜你喜欢

热点阅读