java 线程局部存储,线程局部存储空间 pthread_key_t、__thread 即 ThreadLocal

__thread是GCC内置的线程局部存储设施,__thread变量每一个线程有一份独立实体,各个线程的值互不干扰。可以用来修饰那些带有全局性且值可能变,但是各线程独立不干扰的变量;

只能修饰POD类型(类似整型指针的标量),不能修饰class类型,因为无法自动调用构造函数和析构函数;

可以用于修饰全局变量,函数内的静态变量,不能修饰函数的局部变量或者class的普通成员变量;

且__thread变量值只能初始化为编译器常量。#include #include #include #include #include __thread uint64_t pkey = 0;void run2( ){

FILE* fp = NULL;    if( !pkey )

{        char fName[128] = "";        sprintf( fName, "thread%lu.log", static_cast( pthread_self() ) );

fp   = fopen( fName, "w" );

pkey = reinterpret_cast( fp );

}else fp = reinterpret_cast( pkey );    fprintf( fp, "hello __thread 2\n" );    return ;

}void* run1( void* arg ){

FILE* fp = NULL;    if( !pkey )

{        char fName[128] = "";        sprintf( fName, "thread%lu.log", static_cast( pthread_self() ) );

fp   = fopen( fName, "w" );

pkey = reinterpret_cast( fp );

}else fp = reinterpret_cast( pkey );    fprintf( fp, "hello __thread 1\n" );

run2();    return NULL;

}int main(int argc, char const *argv[]){    char fName[128] = "";    sprintf( fName, "thread%lu.log", static_cast( pthread_self() ) );

FILE* fp = fopen( fName, "w" );

pkey = reinterpret_cast( fp );    fprintf( fp, "hello __thread\n" );    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_tpthread_key_t 优于 __thread 从下面几个方面来说:依赖 linux 环境的 libpthread, 而非 gcc 编译器可移植性增强

如上所示,可以认为对每个 pthread_key, 库内部提供了一个 __thread void* 接受 pthread_setspecific 设置的指针,从而可以指向 class 类型

pthread_key_t 可以作为函数的局部变量,也可以作为局部变量。#include 

// pthread_key_t, pthread_setspecific, pthread_getspecific, pthread_self

// pthread_key_create, pthread_key_delete, pthread_create, pthread_join#include #include #include 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 \n", static_cast( pthread_self() ) );    if( arg ) fclose( reinterpret_cast(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( pthread_getspecific( pkt) );    fprintf( logHandle, "%s\n", 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(pthread_self()) );

logHandle = fopen( fileName, "w");

pthread_setspecific( pkt, reinterpret_cast( 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 resourceint 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\n");    return 0;

}

ThreadLocal对 pthread_key_t 进行了 RAII 的封装,使用更加安全。#include #include     // noncopyable#include  // check_delete#include #include #include #include templateclass ThreadLocal : public boost::noncopyable

{    public:    typedef ThreadLocal* pThreadLocal;

ThreadLocal()

{ pthread_key_create( &pkey_, &ThreadLocal::destroy ); }

~ThreadLocal()

{ pthread_key_delete( pkey_ ); }    T& value()

{

T* pvalue = reinterpret_cast( pthread_getspecific( pkey_ ) );        if( !pvalue )

{

T* obj = new T();

pthread_setspecific( pkey_, reinterpret_cast( obj ) );

pvalue = obj;

}        return *pvalue;

}    private:    static void destroy( void* arg )

{

T* obj = reinterpret_cast( arg );

boost::checked_delete( obj );

}    pthread_key_t pkey_;

};class Logger{

public:

Logger()

{        char fName[128] = "";        sprintf(  fName, "log_%lu.log", static_cast( 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\n", s.c_str() ); }    private:

FILE* fp;

};void* run( void* arg ){    auto ptllogger  = reinterpret_cast::pThreadLocal>( arg);

Logger& plogger = ptllogger->value();

plogger.log( "Hello thread local" );

}int main(){

ThreadLocal::pThreadLocal p = new ThreadLocal;

Logger& plogger = p->value();

plogger.log( "Hello thread local" );    pthread_t threads[2] = {0};

pthread_create( &threads[0], NULL, run, reinterpret_cast( p ) );

pthread_create( &threads[1], NULL, run, reinterpret_cast( p ) );

pthread_join( threads[0], NULL );

pthread_join( threads[1], NULL );    delete p;

}

附录

C++ 获取类中成员函数的函数指针class A {public:    static void staticmember(){cout<

void nonstatic(){cout<

virtual void virtualmember(){cout<

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上行转换(把子类的指针或引用转换成基类表示), 下行转换(把基类指针或引用转换成子类表示)

类指针或引用的上行转换static_cast 和 dynamic_cast 都可以

类指针或引用的下行转换用dynamic_cast并且判断转换后是否为空

基本数据类型之间的转换用static_cast, 但是由于数值范围的不同,需要用户保证转换的安全性

不同类型之间的指针或引用的转换用reinterpret_cast,它的本质是对指向内存的比特位的重解释

消除数据的const、volatile、__unaligned属性,用const_cast

作者:呆呆的张先生

链接:https://www.jianshu.com/p/495ea7ce649b

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值