线程局部存储空间
pthread_key_t、__thread 即 ThreadLocal
来源 https://www.jianshu.com/p/495ea7ce649b
2018.08.30 00:23:52字数 583阅读 1421
__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++ 获取类中成员函数的函数指针
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