分类:
2008-12-16 10:21:39
12.6 thread-specific
data
各个thread是共享process的内存空间的,因此,他们对数据的访问要synchroniize。而有了thread-specifc
data, 就可以保证你所处理的数据是这个thread本身所特有的,而不会影响到别的thread。Errno就是这样的一个例子,我们当然想让errno只反映各个thread的出错情况,而不是整个process。这样就要求每个thread都应该有一份errno。而在thread模型里,errno的确就是被实现为thread-specifc数据。
不要以为thread-specifig的的确确实现了只能各自的thread访问,由于thread共享内存空间的本质,这些thread-specific数据其实还是可以被别的thread访问的,只不过他提供的接口,使得一般情况下不会造成跨越thread的访问。
当然,我们可以这样实现thread specifg数据:你可以自己管理一个table, table的index是各个thread的id,各个table项目里,保存每个thread的thread-specifg数据,这样也可以。但是由于thread id本身可能不是用整形数据来实现的,所以它不适合做index。所以这种方法被抛弃。使用系统提供的这一套接口,我认为,它实际上也是根据当前的thread的id,来查找一个表,表中保存了各个id对应的数据。用一个key来标示一个表,将查找和维护者张表的操作交给了thread库本身。降低了用户的复杂度。
#include int pthread_key_create(pthread_key_t *keyp,
void (*destructor)(void *)); |
Returns: 0 if OK, error number on failure |
这个函数调用一次,就会生成一个key,这个key是被所有的thread都能看到的,并且每个thread会与该key结合一个指针,指向每个thread的thread-specific数据。这个指针,一般情况下,由各个thread去设置,一般都是malloc出来的空间。如果要建立一个key,这个函数只能被调用一次,为了保证不出现race condition,可以使用pthread_once函数,这个可以保证将函数调用一次的功能。
还使用了destructor来制定析构操作。即当一个thread调用pthread_exit的时候,或者return的时候,并且该key对应的本thread的thread specific data指针不为空,那么就会调用这个析构函数。但是,如果你调用了exit之类的函数,就不会调用这些析构了,我个人的想法是,exit之类的函数,是标准c库的函数,由于没有执行pthread_exit函数,pthread库无法知道你要退出,所以就不能在你退出前作任何操作了。所以就不会执行析构操作。况且exit之类函数就直接将整个进程结束掉了。
这里还有一个概念:如果有多个thread specific data,那么他们会被循环检查,一一调用析构函数。如果某些析构函数又再次建立了新的thread specific data.就造成了一遍析构并没有将所有的数据释放,pthread库会自动检查是否还有没有释放的,如果有,会再次来一遍,往复下去,但是有一个上限:PTHREAD_DESTRUCTOR_ITERATIONS。
#include pthread_once_t initflag = PTHREAD_ONCE_INIT; int pthread_once(pthread_once_t *initflag, void (*initfn)(void)); |
Returns: 0 if OK, error number on failure |
这就是保证一个函数只执行一次的方法,就算你在每个thread function里都调用pthread_once
,initfn只被执行一次。
如下是一段例子:
void destructor(void *);
pthread_key_t key;
pthread_once_t init_done = PTHREAD_ONCE_INIT;
void
thread_init(void)
{
err = pthread_key_create(&key, destructor);
}
int
threadfunc(void *arg)
{
pthread_once(&init_done, thread_init);
...
}
如下是给一个key设置thread specific数据的方法:
#include void *pthread_getspecific(pthread_key_t key); |
Returns: thread-specific data value or NULL if no value |
int pthread_setspecific(pthread_key_t key, const void *value); |
Returns: 0 if OK, error number on failure |
下面我们验证在另一个thread里面,是可以修改其他thread的thread specific数据的。这个例子,生成2个thread,他们各自有一个specific数据。并且他们将其数据地址放到了specific_data数组里面,这样别的thread就可以访问他了。还可以修改。可以看出,thread_specific数据本身就是一套方便实用的接口,并没有什么特殊的效果。
#include
#include
#include
#include
static pthread_key_t
key;
static pthread_once_t
init_once = PTHREAD_ONCE_INIT;
pthread_mutex_t mutex =
PTHREAD_MUTEX_INITIALIZER;
void* specific_data[2]
={0};
void init( )
{
puts("this is pthread once init function, key is
created");
pthread_key_create( &key, free );
}
void* thread( void* para
)
{
pthread_once( &init_once, init );
pthread_t t = pthread_self();
printf("thread %d\n", t);
char* sp = (char*)malloc( 10 );
pthread_setspecific( key, (void*)sp );
printf("thread %d: specific data: %d\n", (unsigned
long)t, (unsigned long)sp );
//store thread specific data into array
pthread_mutex_lock(&mutex);
int i=0;
if ( !specific_data[0] )
i = 0;
else
i = 1;
specific_data[i] = sp;
*sp = i;
pthread_mutex_unlock(&mutex);
if( i == 0 )
{
sleep(1);
//after one thread sleeps, we are sure ,all thread have
stored their specific data into
//the array
printf("thread %d, before editing,
*(specific_data[0])=%d, *(specific_data[1])=%d\n",(unsigned long)t,
*((char*)specific_data[0]),*((char*)specific_data[1]) );
*((char*)specific_data[1]) = 0;
printf("thread %d, after editing,
*(specific_data[0])=%d, *(specific_data[1])=%d\n",(unsigned long)t,
*((char*)specific_data[0]),*((char*)specific_data[1]) );
}
else
sleep(10);
pthread_exit(0);
}
int main()
{
pthread_t t1,t2;
void *st1, *st2;
pthread_create( &t1, NULL, thread, NULL );
pthread_create( &t2, NULL, thread, NULL );
pthread_join( t1, &st1 );
pthread_join( t2, &st2 );
printf("thread %d returns %d\n", t1, (unsigned
long)st1 );
printf("thread %d returns %d\n", t2, (unsigned long
)st2);
return 0;
}
运行结果:
[shaoting@serverbj6:/user/shaoting/advanced_programming_under_unix_environment_2rd/mytest]$
./a.out
this is pthread once
init function, key is created
thread 1084229952
thread 1084229952:
specific data: 135598704
thread 1094719808
thread 1094719808:
specific data: 135598736
thread 1084229952,
before editing, *(specific_data[0])=0, *(specific_data[1])=1
thread 1084229952, after
editing, *(specific_data[0])=0, *(specific_data[1])=0
thread 1084229952
returns 0
thread 1094719808
returns 0