美文网首页
Linux线程局部变量实现

Linux线程局部变量实现

作者: 突击手平头哥 | 来源:发表于2021-01-06 00:15 被阅读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);           //取出线程独享数据
        }
        ...
    }
    
    • pthread_key_create:第二个参数可以指定线程退出时执行的函数,其参数就是pthread_setspecific设置的指针
    • pthread_key_delete:并不会释放资源

    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
    

    相关文章

      网友评论

          本文标题:Linux线程局部变量实现

          本文链接:https://www.haomeiwen.com/subject/ilvgoktx.html