APUE——线程私有数据

APUE链接
线程私有数据链接1
pthread_once链接

1. 线程私有数据

进程中的所有线程都可以访问进程的整个地址空间。除了使用寄存器以外,线程没有办法阻止其他线程访问它的数据,线程私有数据也不例外

线程私有数据(也称线程特定数据)是存储和查询与某个线程相关的数据的一种机制。把这种数据称为线程私有数据或线程特定数据的原因是:希望每个线程可以独立地访问数据副本,而不需要担心与其他线程的同步访问问题。
errno被重新定义为线程私有数据。这样,一个线程做了设置errno的操作并不会影响进程中其他线程的errno的值。

在分配线程私有数据之前,需要创建与该数据关联的键。这个键将用于获取对线程私有数据的访问权。使用pthread_key_create创建一个键。
destructor为析构函数,为线程正常退出的时候调用,比如pthread_exit,return,cancel,注意不能是exit等,或者是非正常退出,http://www.cnblogs.com/nufangrensheng/p/3509618.html
线程退出时,线程私有数据的析构函数将按照操作系统实现中定义的顺序被调用。析构函数可能会调用另一个函数,该函数可能会创建新的线程私有数据而且把这个数据与当前的键关联起来。当所有的析构函数都调用完成以后,系统会检查是否还有非null的线程私有数据值与键关联,如果有的话,再次调用析构函数。这个过程会一直重复直到线程所有的键都为null值线程私有数据,或者已经做了PTHREAD_DESTRUCTOR_ITERATIONS(http://www.cnblogs.com/nufangrensheng/p/3522577.html中表12-1)中定义的最大次数的尝试。

  1. 创建一个键 int pthread_key_create(pthread_key_t * key, void ( * destructor)(void*))
  2. 为一个键设置线程私有数据 int pthread_setspecific(pthread_key_t key,const void *pointer));
  3. 从一个键读取线程私有数据 void *pthread_getspecific(pthread_key_t key);
  4. 线程退出(退出时,会调用destructor释放分配的缓存,参数是key所关联的数据) void pthread_exit(void*value_ptr)
  5. 删除一个键 int pthread_key_delete(pthread_key_t key),键占用的内存被释放。与该键关联的线程数据所占用的内存并不被释放。因此,线程数据的释放(destructor),必须在释放键之前完成。
  6. 如果没有线程私有数据值与键关联,pthread_getspecific将返回一个空指针,可以据此来确定是否需要调用pthread_setspecific。

2. pthread_once

pthread_once保证该函数在进程中只执行一次

#include <pthread.h>
pthread_once_t initflag = PTHREAD_ONCE_INIT;
int pthread_once(pthread_once_t *initflag, void (*initfn)(void));
返回值:若成功则返回0,否则返回错误编号
int pthread_once(pthread_once_t *once_control, void (*init_routine) (void));

功能:本函数使用初值为PTHREAD_ONCE_INIT的once_control变量保证init_routine()函数在本进程执行序列中仅执行一次。
在多线程编程环境下,尽管pthread_once()调用会出现在多个线程中,init_routine()函数仅执行一次,究竟在哪个线程中执行是不定的,是由内核调度来决定。

下列代码保证pthread_key_create只执行一次,确保key值是相同的

void destructor(void *);

pthread_key_t key;
thread_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);
    ...
}

3. 例子分析

#include <limits.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>

static pthread_key_t key;
static pthread_once_t init_done = PTHREAD_ONCE_INIT;
pthread_mutex_t env_mutex = PTHREAD_MUTEX_INITIALIZER;

extern char **environ;

static void
pthread_init(void)
{
    pthread_key_create(&key, free);
}

char *
getenv(const char *name)
{
    int     i, len;
    char     *envbuf;

    pthread_once(&init_done, thread_init);
    pthread_mutex_lock(&env_mutex);
    envbuf = (char *)pthread_getspecific(key);
    if(envbuf == NULL)
    {
        envbuf = malloc(ARG_MAX);
        if(envbuf == NULL)
        {
            pthread_mutex_unlock(&env_mutex);
            return(NULL);
        }
        pthread_setspecific(key, envbuf);
    }
    len = strlen(name);
    for(i = 0; environ[i] != NULL; i++)
    {
        if((strncmp(name, environ[i], len) == 0) &&
           (environ[i][len] == '='))
        {
            strcpy(envbuf, &environ[i][len+1]);
            pthread_mutex_unlock(&env_mutex);
            return(envbuf);
        }
    }    
    pthread_mutex_unlock(&env_mutex);
    return(NULL);
}
/*三个线程:主线程,th1,th2各自有自己的私有数据区域
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>

static pthread_key_t str_key;
//define a static variable that only be allocated once
static pthread_once_t str_alloc_key_once=PTHREAD_ONCE_INIT;
static void str_alloc_key();
static void str_alloc_destroy_accu(void* accu);

char* str_accumulate(const char* s)
{    char* accu;
    
    pthread_once(&str_alloc_key_once,str_alloc_key);//str_alloc_key()这个函数只调用一次
    accu=(char*)pthread_getspecific(str_key);//取得该线程对应的关键字所关联的私有数据空间首址
    if(accu==NULL)//每个新刚创建的线程这个值一定是NULL(没有指向任何已分配的数据空间)
    {    accu=malloc(1024);//用上面取得的值指向新分配的空间
        if(accu==NULL)    return NULL;
        accu[0]=0;//为后面strcat()作准备
      
        pthread_setspecific(str_key,(void*)accu);//设置该线程对应的关键字关联的私有数据空间
        printf("Thread %lx: allocating buffer at %p\n",pthread_self(),accu);
     }
     strcat(accu,s);
     return accu;
}
//设置私有数据空间的释放内存函数
static void str_alloc_key()
{    pthread_key_create(&str_key,str_alloc_destroy_accu);/*创建关键字及其对应的内存释放函数,当进程创建关键字后,这个关键字是NULL。之后每创建一个线程os都会分给一个对应的关键字,关键字关联线程私有数据空间首址,初始化时是NULL*/
    printf("Thread %lx: allocated key %d\n",pthread_self(),str_key);
}
/*线程退出时释放私有数据空间,注意主线程必须调用pthread_exit()(调用exit()不行)才能执行该函数释放accu指向的空间*/
static void str_alloc_destroy_accu(void* accu)
{    printf("Thread %lx: freeing buffer at %p\n",pthread_self(),accu);
    free(accu);
}
//线程入口函数
void* process(void *arg)
{    char* res;
    res=str_accumulate("Resule of ");
    if(strcmp((char*)arg,"first")==0)
        sleep(3);
    res=str_accumulate((char*)arg);
    res=str_accumulate(" thread");
    printf("Thread %lx: \"%s\"\n",pthread_self(),res);
    return NULL;
}
//主线程函数
int main(int argc,char* argv[])
{    char* res;
    pthread_t th1,th2;
    res=str_accumulate("Result of ");
    pthread_create(&th1,NULL,process,(void*)"first");
    pthread_create(&th2,NULL,process,(void*)"second");
    res=str_accumulate("initial thread");
    printf("Thread %lx: \"%s\"\n",pthread_self(),res);
    pthread_join(th1,NULL);
    pthread_join(th2,NULL);
    pthread_exit(0);
}

猜你喜欢

转载自blog.csdn.net/weixin_44537992/article/details/106203426