一
概述
现在多核时代多线程开发越来越重要了多线程相比于多进程有诸多优势(当然也有诸多劣势)在早期C的库中有许多函数是线程不安全的因为内部用到了静态变量比如char *strtok(char *s const char *delim) 该函数内部就有一个静态指针如果多个线程同时调用此函数时可能就会出现奇怪的结果当然也不是我们所想要的现在LINUX对此函数功能有一个线程安全版本的接口char *strtok_r(char *s const char *delim char **ptrptr)这就避免了多个线程同时访问的沖突问题其实如果保持 strtok()/ 接口不变同时还要保证线程安全还有一个解决办法那就是采用线程局部变量
使用线程局部变量有两种使用方式一个稍微麻烦些一个比较简单下面一一做个介绍(以LINUX为例)
二线程局部变量的使用
比较麻烦些的使用方法用到的函数主要有三个pthread_once(pthread_once_t* void (*init_routine)(void)) pthread_key_create()/ pthread_setspecific()/ pthread_getspecific()/其中 pthread_once 可以保证在整个进程空间init_routine函数仅被调用一次(它解决了多线程环境中使得互斥量和初始化代码都仅被初始化一次的问题)pthread_key_create 的参数之一指一个析构函数指针当某个线程终止时该析构函数将被调用并用对于一个进程内的给定键该函数只能被调用一次pthread_sespecific 和 pthread_getspecific 用来存放和获取与一个键关联的值例子如下
pthread_key_t key;
pthread_once_t once = PTHREAD_ONCE_INIT;
static void destructor(void *ptr)
{
free(ptr)
}
void init_once(void)
{
pthread_key_create(&key destructor)
}
static void *get_buf(void)
{
pthread_once(&once init_once)
if ((ptr = pthread_getspecific(key)) == NULL) {
ptr = malloc()
pthread_setspecific(key ptr)
}
return (ptr)
}
static void *thread_fn(void *arg)
{
char *ptr = (char*) get_buf()
sprintf(ptr hello world)
printf(》%s\n ptr)
return (NULL)
}
void test(void)
{
int i n = ;
pthread_t tids[];
for (i = ; i < n; i++) {
pthread_create(&tids[i] NULL thread_fn NULL)
}
for (i = ; i < n; i++) {
pthread_join(&tids[i] NULL)
}
}
另外还有一个更加简单使用线程局部变量的方法__thread 修饰符 (在WIN平台下需要用 __declspec(thread) 修饰符WIN的东东总得要多写几笔呵呵)于是上述代码可以修改如下
static void *get_buf(void)
{
static __thread void *ptr = malloc()
return (ptr)
}
static void *thread_fn(void *arg)
{
char *ptr = (char*) get_buf()
sprintf(ptr hello world)
printf(》%s\n ptr)
return (NULL)
}
void test(void)
{
int i n = ;
pthread_t tids[];
for (i = ; i < n; i++) {
pthread_create(&tids[i] NULL thread_fn NULL)
}
for (i = ; i < n; i++) {
pthread_join(&tids[i] NULL)
}
}
看到没有这段代码比前面一个简单许多但却有一个问题它存在内存洩露问题因为当线程退出时各个线程分配的动态内存(ptr = malloc()) 并没有被释放
三用ACL线程接口操作线程局部变量
为了解决上述问题ACL库中实现了线程局部变量的简单释放功能acl_pthread_atexit_add(void *arg void (*free_callback)(void*))修改上述代码如下
static void free_fn(void *ptr)
{
free(ptr)
}
static void *get_buf(void)
{
static __thread void *ptr = malloc()
acl_pthread_atexit_add(ptr free_fn)
return (ptr)
}
static void *thread_fn(void *arg)
{
char *ptr = (char*) get_buf()
sprintf(ptr hello world)
printf(》%s\n ptr)
return (NULL)
}
void test(void)
{
int i n = ;
pthread_t tids[];
for (i = ; i < n; i++) {
acl_pthread_create(&tids[i] NULL thread_fn NULL)
}
for (i = ; i < n; i++) {
acl_pthread_join(&tids[i] NULL)
}
}
ok 一切问题得到解决细心的读者会发现 pthread_create pthread_join 前面都加了前缀 acl_ 这是因为 ACL库对线程库进行了封装以适应不同平台下(UNIXWIN)下的使用这个例子是跨平台的WIN下同样可用