線程局部存儲空間

線程局部存儲空間

pthread_key_t、__thread 即 ThreadLocal

來源  https://www.jianshu.com/p/495ea7ce649bhtml

2018.08.30 00:23:52字數 583閱讀 1421
 

__thread

參考: 線程局部變量 __thread 關鍵字linux

  • __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\n" );
    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\n" );

    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\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_t

參考:關鍵字:__thread & pthread_key_tios

pthread_key_t 優於 __thread 從下面幾個方面來講:c++

  • 依賴 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 \n", 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\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<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\n");
    return 0;
}

ThreadLocal

參考:關鍵字:__thread & pthread_key_t安全

對 pthread_key_t 進行了 RAII 的封裝,使用更加安全。app

#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\n", 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++對象模型之指向成員函數的指針ide

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

 

================ Endui

相關文章
相關標籤/搜索