线程私有数据
每个线程可以独立地访问数据副本,而不需要担心与其他线程的同步访问问题。
它提供了让基于进程的接口适应多线程环境的机制,一个很明显的实例就是errno。为了让线程也能够使用那些原本基于进程的系统调用和库例程,errno被重新定义为线程私有数据。这样,一个线程设置errno的操作并不会影响进程中其他线程的errno值。
int pthread_key_create(pthread_key_t *keyp, void (*destructor)(void *));
“键”可以被进程中的所有线程使用,而每个线程把这个键与不同的线程私有数据地址进行关联,这就是其基本原理。
errno的定义
/* Declare the `errno' variable, unless it's defined as a macro by
bits/errno.h. This is the case in GNU, where it is a per-thread
variable. This redeclaration using the macro still works, but it
will be a function declaration without a prototype and may trigger
a -Wstrict-prototypes warning. */
#ifndef errno
extern int errno;
#endif
在<bits/errno.h>头文件中:
# ifndef __ASSEMBLER__
/* Function to get address of global `errno' variable. */
extern int *__errno_location (void) __THROW __attribute__ ((__const__));
# if !defined _LIBC || defined _LIBC_REENTRANT
/* When using threads, errno is a per-thread value. */
# define errno (*__errno_location ())
# endif
# endif /* !__ASSEMBLER__ */
errno实际上,并不是我们通常认为的是个整型数值,而是通过整型指针来获取值的。这个整型就是线程安全的。
另外,宏之所以这样实现,是因为标准库规定了必须能够通过&errno方式取得保存错误代码的变量的地址,因此__errno_location()函数的返回值是指针,并把宏定义为解引用函数返回的地址*__errno_location()。如果__errno_location直接返回int类型,此时就无法取得保存错误代码的变量的地址。
errno的可能实现
#include<pthread.h>
static pthread_key_t key;
static pthread_once_t init_done = PTHREAD_ONCE_INIT;
static void
thread_init(void)
{
pthread_key_create(&key, free);
}
int *
__errno_location_simply_fake(void)
{
int *errp;
/* 保证多线程环境里 key 只被创建一次 */
pthread_once(&init_done, thread_init);
errp = (int *)pthread_getspecific(key);
if(errp == NULL) {
errp = malloc(sizeof(int));
if(errp != NULL)
pthread_setspecific(key, errp);
}
return errp;
}
参考:
1. 《APUE》第12.6节 线程私有数据
2. blog.csdn.net/romandion/archive/2008/01/11/2036975.aspx
3. bbs.chinaunix.net/viewthread.php