一,一次性初始化
以保证线程在调用资源时,确保资源已经被初始化,并且只初始化一次。
在传统的顺序编程中,一次性初始化经常通过使用布尔变量来管理。控制变量被静态初始化为0,而任何依赖于初始化的代码都能测试该变量。如果变量值仍然为0,则它能实行初始化,然后将变量置为1。以后检查的代码将跳过初始化。
但是在多线程程序设计中,事情就变的复杂的多。如果多个线程并发地执行初始化序列代码,2个线程可能发现控制变量为0,并且都实行初始话,而该过程本该仅仅执行一次。初始化的状态必须由互斥量保护。之所以使用pthread_once,主要原因是原来不能静态的初始化一个互斥量,这样如果要使用一个互斥量,必须调用pthread_mutex_init函数初始化互斥量,并且必须仅仅初始化一次,因此初始化调用应该在一次性初始化代码中完成,pthread_once就解决在多线程环境中使得互斥量和初始化代码都仅仅被初始化一次的问题;
#include <pthread.h> pthread_once_t once_control=PTHREAD_ONCE_INIT; int pthread_once(pthread_once_t *once_control,void(*init_routine)(void)); init_routine 初始化函数实例代码如下:
#include <pthread.h> #include <stdio.h> pthread_once_t once_block = PTHREAD_ONCE_INIT; pthread_mutex_t mutex; /* * This is the one-time initialization routine. It will be * called exactly once, no matter how many calls to pthread_once * with the same control structure are made during the course of * the program. */ void once_init_routine (void) { int status; status = pthread_mutex_init (&mutex, NULL); if (status != 0) printf("error Init Mutex"); } /* * Thread start routine that calls pthread_once. */ void *thread_routine (void *arg) { int status; status = pthread_once (&once_block, once_init_routine); if (status != 0) printf("error Once init"); status = pthread_mutex_lock (&mutex); if (status != 0) printf("error Lock mutex"); printf ("thread_routine has locked the mutex.\n"); status = pthread_mutex_unlock (&mutex); if (status != 0) printf("error Unlock mutex"); return NULL; } int main (int argc, char *argv[]) { pthread_t thread_id; char *input, buffer[64]; int status; status = pthread_create (&thread_id, NULL, thread_routine, NULL); if (status != 0) printf("error Create thread"); status = pthread_once (&once_block, once_init_routine); if (status != 0) printf("error Once init"); status = pthread_mutex_lock (&mutex); if (status != 0) printf("error Lock mutex"); printf ("Main has locked the mutex.\n"); status = pthread_mutex_unlock (&mutex); if (status != 0) printf("error Unlock mutex"); status = pthread_join (thread_id, NULL); if (status != 0) printf("error Join thread"); return 0; }程序输出:
Main has locked the mutex.
thread_routine has locked the mutex.
二,线程私有数据
在单线程程序中,我们经常要用到"全局变量"以实现多个函数间共享数据。在多线程环境下,由于数据空间是共享的,因此全局变量也为所有线程所共有。但有时应用程序设计中有必要提供线程私有的全局变量,仅在某个线程中有效,但却可以跨多个函数访问,比如程序可能需要每个线程维护一个链表,而使用相同的函数操作,最简单的办法就是使用同名而不同变量地址的线程相关数据结构。这样的数据结构可以由Posix线程库维护,称为线程私有数据(Thread-specific Data,或TSD)。
pthread_key_t key; int pthread_key_create(pthread_key *key,void (*destructor)(void*)); int pthread_key_delete(pthread_key_t key);
该函数从TSD池中分配一项,将其值赋给key供以后访问使用。如果destr_function不为空,在线程退出(pthread_exit())时将以key所关联的数据为参数调用destr_function(),以释放分配的缓冲区。不论哪个线程调用pthread_key_create(),所创建的key都是所有线程可访问的,但各个线程可根据自己的需要往key中填入不同的值,这就相当于提供了一个同名而不同值的全局变量.
pthread_key_delete这个函数并不检查当前是否有线程正使用该TSD,也不会调用清理函数(destr_function),而只是将TSD释放以供下一次调用pthread_key_create()使用。因为只有你肯定没有线程持有该键值时,才能删除线程私有数据键,故通常的做法是不释放线程私有数据键。
2)使用线程私有数据
int pthread_setspecific(pthread_key_t key, const void *value) void * pthread_getspecific(pthread_key_t key)可以使用pthread_getspecific函数来获取线程当前键值,或通过第一个函数改变当前的键值。
/* * tsd_once.c * * Demonstrate use of pthread_once to initialize something * exactly once within a multithreaded program. * * Note that it is often easier to use a statically initialized * mutex to accomplish the same result. */ #include <pthread.h> #include <stdio.h> /* * Structure used as the value for thread-specific data key. */ typedef struct tsd_tag { pthread_t thread_id; char *string; } tsd_t; pthread_key_t tsd_key; /* Thread-specific data key */ pthread_once_t key_once = PTHREAD_ONCE_INIT; /* * One-time initialization routine used with the pthread_once * control block. */ void once_routine (void) { int status; printf ("initializing key\n"); status = pthread_key_create (&tsd_key, NULL); if (status != 0) printf("error Create key"); } /* * Thread start routine that uses pthread_once to dynamically * create a thread-specific data key. */ void *thread_routine (void *arg) { tsd_t *value; int status; status = pthread_once (&key_once, once_routine);//一次性初始化 if (status != 0) printf("error Once init"); value = (tsd_t*)malloc (sizeof (tsd_t)); if (value == NULL) printf("error Allocate key value"); status = pthread_setspecific (tsd_key, value); if (status != 0) printf("error Set tsd"); printf ("%s set tsd value %p\n", arg, value); value->thread_id = pthread_self (); value->string = (char*)arg; value = (tsd_t*)pthread_getspecific (tsd_key); printf ("%s starting...\n", value->string); sleep (2); value = (tsd_t*)pthread_getspecific (tsd_key); printf ("%s done...\n", value->string); return NULL; } void main (int argc, char *argv[]) { pthread_t thread1, thread2; int status; status = pthread_create ( &thread1, NULL, thread_routine, "thread 1"); if (status != 0) printf("error Create thread 1"); status = pthread_create ( &thread2, NULL, thread_routine, "thread 2"); if (status != 0) printf("error Create thread 2"); pthread_exit; }运行结果如下:
thread 2 set tsd value 0x9de24f0
thread 2 starting...
thread 1 set tsd value 0x9de2500
thread 1 starting...
thread 2 done...
thread 1 done...