• 线程局部存储空间


    线程局部存储空间

    pthread_key_t、__thread 即 ThreadLocal

    来源  https://www.jianshu.com/p/495ea7ce649b

    2018.08.30 00:23:52字数 583阅读 1421
     

    __thread

    参考: 线程局部变量 __thread 关键字

    • __thread是GCC内置的线程局部存储设施,__thread变量每一个线程有一份独立实体,各个线程的值互不干扰。可以用来修饰那些带有全局性且值可能变,但是各线程独立不干扰的变量;
    • 只能修饰POD类型(类似整型指针的标量),不能修饰class类型,因为无法自动调用构造函数和析构函数;
    • 可以用于修饰全局变量,函数内的静态变量,不能修饰函数的局部变量或者class的普通成员变量;
    • 且__thread变量值只能初始化为编译器常量。
    #include <pthread.h>
    #include <cstdio>
    #include <cstdlib>
    #include <assert.h>
    #include <stdint.h>
    
    __thread uint64_t pkey = 0;
    
    void run2( )
    {
        FILE* fp = NULL;
    
        if( !pkey )
        {
            char fName[128] = "";
            sprintf( fName, "thread%lu.log", static_cast<unsigned long>( pthread_self() ) );
            fp   = fopen( fName, "w" );
            pkey = reinterpret_cast<uint64_t>( fp ); 
    
        }else fp = reinterpret_cast<FILE*>( pkey );
    
        fprintf( fp, "hello __thread 2
    " );
        return ;
    }
    
    void* run1( void* arg )
    {
        FILE* fp = NULL;
    
        if( !pkey )
        {
            char fName[128] = "";
            sprintf( fName, "thread%lu.log", static_cast<unsigned long>( pthread_self() ) );
            fp   = fopen( fName, "w" );
            pkey = reinterpret_cast<uint64_t>( fp ); 
    
        }else fp = reinterpret_cast<FILE*>( pkey );
    
        fprintf( fp, "hello __thread 1
    " );
    
        run2();
    
        return NULL;
    }
    
    int main(int argc, char const *argv[])
    {
        char fName[128] = "";
        sprintf( fName, "thread%lu.log", static_cast<unsigned long>( pthread_self() ) );
        FILE* fp = fopen( fName, "w" );
        pkey = reinterpret_cast<uint64_t>( fp );
        fprintf( fp, "hello __thread
    " );
    
        pthread_t threads[2];
        pthread_create( &threads[0], NULL, run1, NULL );
        pthread_create( &threads[1], NULL, run1, NULL );
        pthread_join( threads[0], NULL );
        pthread_join( threads[1], NULL );
        return 0;
    }
    
    

    pthread_key_t

    参考:关键字:__thread & pthread_key_t

    pthread_key_t 优于 __thread 从下面几个方面来说:

    • 依赖 linux 环境的 libpthread, 而非 gcc 编译器可移植性增强
    • 如上所示,可以认为对每个 pthread_key, 库内部提供了一个 __thread void* 接受 pthread_setspecific 设置的指针,从而可以指向 class 类型
    • pthread_key_t 可以作为函数的局部变量,也可以作为局部变量。
    #include <pthread.h> 
        // pthread_key_t, pthread_setspecific, pthread_getspecific, pthread_self
        // pthread_key_create, pthread_key_delete, pthread_create, pthread_join
    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    
    using namespace std;
     
    static pthread_key_t pkt;
    // 1, callback function to destroy resource associated with key
    // 2, the in_param is pthread_getspecific()
    // 3, gettid()是内核给线程(轻量级进程)分配的进程id,全局(所有进程中)唯一
    // 4, pthread_self()是在用户态实现的,获取的id实际上是主线程分配给子线程的线程描述符的地址而已,只是在当前进程空间中是唯一的。
    void destroy( void *arg )
    {
        printf("exit at thread %d, fclose file 
    ", static_cast<int>( pthread_self() ) );
        if( arg ) fclose( reinterpret_cast<FILE*>(arg) );
    }
    // 5, pthread_getspecific() Return current value of the thread-specific data slot identified by KEY.
    void writeLog( const char* log )
    {
        FILE* logHandle = reinterpret_cast<FILE*>( pthread_getspecific( pkt) );
        fprintf( logHandle, "%s
    ", log );
    }
    // 6, pthread_setspecific Store POINTER in the thread-specific data slot identified by KEY 
    void* work( void* arg)
    {
        FILE* logHandle = NULL;
        char fileName[128] = "";
        sprintf( fileName, "Thread%d.log", static_cast<int>(pthread_self()) );
        logHandle = fopen( fileName, "w");
        pthread_setspecific( pkt, reinterpret_cast<void*>( logHandle ) );
        writeLog( "Thread starting." );
    }
    // 7, pthread_key_create( &pkt, destroy ) Create a key value identifying a location in the thread-specific      //identifying 识别
    //    data area. Each thread maintains a distinct thread-specific data area.
    //    the destroy callback function will called with the key is dectroyed
    // 8, pthread_key_delete( ) detroy the key use callback function clear the resource
    int main(int argc, char const *argv[])
    {
        pthread_key_create( &pkt, destroy );
        pthread_t pids[2] = {0};
        pthread_create( &pids[0], NULL, work, NULL );
        pthread_create( &pids[1], NULL, work, NULL );
        pthread_join( pids[0], NULL );
        pthread_join( pids[1], NULL );
        pthread_key_delete( pkt );
        printf("stop
    ");
        return 0;
    }
    

    ThreadLocal

    参考:关键字:__thread & pthread_key_t

    对 pthread_key_t 进行了 RAII 的封装,使用更加安全。

    #include <pthread.h>
    #include <boost/noncopyable.hpp>    // noncopyable
    #include <boost/checked_delete.hpp> // check_delete
    #include <cstdio>
    #include <cstdlib>
    #include <string>
    #include <stdexcept>
    
    template<typename T>
    class ThreadLocal : public boost::noncopyable
    {
        public:
        typedef ThreadLocal<T>* pThreadLocal;
        ThreadLocal()
        { pthread_key_create( &pkey_, &ThreadLocal::destroy ); }
    
        ~ThreadLocal()
        { pthread_key_delete( pkey_ ); }
    
        T& value()
        {
            T* pvalue = reinterpret_cast<T*>( pthread_getspecific( pkey_ ) );
            if( !pvalue )
            {
                T* obj = new T();
                pthread_setspecific( pkey_, reinterpret_cast<void*>( obj ) );
                pvalue = obj;
            }
            return *pvalue;
        }
    
        private:
        static void destroy( void* arg )
        { 
            T* obj = reinterpret_cast<T*>( arg );
            boost::checked_delete( obj );
        }
    
        pthread_key_t pkey_;
    };
    
    class Logger
    {
        public:
        Logger()
        {
            char fName[128] = "";
            sprintf(  fName, "log_%lu.log", static_cast<unsigned long>( pthread_self() ) );
            fp = fopen( fName, "w" );
            if( !fp ) throw std::runtime_error( std::string("can not create ") + fName );
        }
    
        ~Logger() { fclose( fp ); }
    
        void log( const std::string& s ) { fprintf( fp, "%s
    ", s.c_str() ); }
    
        private:
        FILE* fp;
    };
    
    void* run( void* arg )
    {
        auto ptllogger  = reinterpret_cast< ThreadLocal<Logger>::pThreadLocal>( arg);
        Logger& plogger = ptllogger->value();
        plogger.log( "Hello thread local" );
    }
    
    int main()
    {
        ThreadLocal<Logger>::pThreadLocal p = new ThreadLocal<Logger>;
        Logger& plogger = p->value();
        plogger.log( "Hello thread local" );
    
        pthread_t threads[2] = {0};
        pthread_create( &threads[0], NULL, run, reinterpret_cast<void*>( p ) );
        pthread_create( &threads[1], NULL, run, reinterpret_cast<void*>( p ) );
        pthread_join( threads[0], NULL );
        pthread_join( threads[1], NULL );
        delete p;
    }
    

    附录

    C++ 获取类中成员函数的函数指针

    参见:深入探索C++对象模型之指向成员函数的指针

    class A 
    {
    public:
        static void staticmember(){cout<<"static"<<endl;}   //static member
        void nonstatic(){cout<<"nonstatic"<<endl;}          //nonstatic member
        virtual void virtualmember(){cout<<"virtual"<<endl;};//virtual member
    };
    int main()
    {
        A a;
        //static成员函数,取得的是该函数在内存中的实际地址,而且因为static成员是全局的,所以不能用A::限定符
        void (*ptrstatic)() = &A::staticmember;      
        //nonstatic成员函数 取得的是该函数在内存中的实际地址     
        void (A::*ptrnonstatic)() = &A::nonstatic;
        //虚函数取得的是虚函数表中的偏移值,这样可以保证能过指针调用时同样的多态效果
        void (A::*ptrvirtual)() = &A::virtualmember;
        //函数指针的使用方式
        ptrstatic();
        (a.*ptrnonstatic)();
        (a.*ptrvirtual)();
    }
    

    static_cast, dynamic_cast, reinterpret_cast, const_cast

    参见:c++ 数据类型转换: static_cast dynamic_cast reinterpret_cast const_cast

    • 上行转换(把子类的指针或引用转换成基类表示), 下行转换(把基类指针或引用转换成子类表示)
    • 类指针或引用的上行转换static_cast 和 dynamic_cast 都可以
    • 类指针或引用的下行转换用dynamic_cast并且判断转换后是否为空
    • 基本数据类型之间的转换用static_cast, 但是由于数值范围的不同,需要用户保证转换的安全性
    • 不同类型之间的指针或引用的转换用reinterpret_cast,它的本质是对指向内存的比特位的重解释
    • 消除数据的const、volatile、__unaligned属性,用const_cast

    ================ End

  • 相关阅读:
    [笔记] Duke
    foxmail配置office365邮箱
    bootstrap-select and selectpicker 修改下拉框的宽度或者下方留白
    项目技术沉淀0
    alibaba开发手册
    关于fastjson
    org.springframework.jdbc.UncategorizedSQLException: ### Error updating database. Cause: java.sql.SQLException: Incorrect string value: 'xE2x80x8BxE2x80x8B...' for column 'pro_backgroud' at row
    datatable某列不排序、和自定义搜索、给数据里面加属性
    19.10.30笔记-文件下载
    没有图片的freemarker下载,备份
  • 原文地址:https://www.cnblogs.com/lsgxeva/p/11591818.html
Copyright © 2020-2023  润新知