线程的终止和私有数据

linux下线程的终止有有两种方式:

1.通过return从线程函数返回. 2.通过掉用 pthread_exit()使线程退出,pthread_exit在头文件 pthread.h中申明. 函数原型:void pthread_exit(void * retval); 注意,有两种特殊的情况,第一种情况是,在主线程中,如果从main函数返回 或是调用了exit()函数退出主线程,则整个进程都将终止,此时进程中所有的线程也将终止,因此在主线程中不能过早的从main函数中返回.另一种是如果主线程调用 pthread_exit()函数,仅仅是主线程消亡,进程不会结束. 线程终止最重要的问题是资源释放的问题,特别是一些临界资源,临界资源在一段时间内只能被一个线程所持有,当其它线程要使用临界资源时提出请求,如果该资源未被使用则申请成功,否则等待,linux下提供了一对函数pthread_cleanup_push(),pthread_cleaned_pop()用于自动释放资源,两个函数之间的终止线程的动作都将执行清理函数. 线程的等待函数pthread_join(pthread_t th,void *thread_return,)

#include <stdio.h>                                                                                                                              
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
const char *message="yanglongfei";
void thread(void *arg)
{
        pthread_t newphid;
        newphid=pthread_self();
        printf("我是新线程,我的ID = %u,文章=%s\n",(unsigned)newphid,(char*)arg);
        sleep(3);
        pthread_exit(0);
}
int main()
{
        pthread_t thid;
        int status;
        pthread_create(&thid,NULL,(void *)&thread,(void*)message);
        pthread_join(thid,(void *)&status);
        printf("新线程的退出码是:%d\n",status);
        pthread_exit(0);
}

结果:

yang@liu:~/syc/第八章$ ./pthreadgetset1
我是新线程,我的ID = 729642752,文章=yanglongfei
新线程的退出码是:0

第一句输出后,等待了三秒,才结束.

2.私有数据:

1.概念及作用:在单线程中,我们经常要用到"全局变量"以实现多个函数间共享数据,在多线程的环境下,由于数据空间是共享的,因此全局变量也为所有线程所共有。但有时应用程序设计中有必要提供线程私有的全局变量,仅在某个线程中有效,但却可以跨多个函数访问,比如 程序可能需要每个线程维护一个链表,而使用相同的函数操作,最简单的办法就是使用同名而不同变量地址的线程相关数据结构。这样的数据结构可以由Posix 线程库维护,称为线程私有数据(Thread-specific Data,或TSD)。 在分配线程私有数据之前,要创建与该数据相关联的key. 2.int pthread_key_create(pthread_key_t *keyp,void(*destructor)(void *));  返回值:若成功返回0,否则返回错误编码。 需要注意的是:创建的key存放在keyp指向的内存单元,所以为了方便最好把它定义为一个全局变量。如果创建一个线程的私有数据,必须保证pthread_key_create对于每个pthread_key_t变量仅仅被调用一次,因为一个键入果被创建两次,其实是在创建两个不同的键,第二个键将覆盖第一个键,第一个键以及任何线程为其关联的线程私有数据将丢失. 2.关于析构函数void (*destructor)(void *); 当线程退出时,如果线程的私有数据地址被置为NULL,那么析构函数就不会被调用.当线程调用pthread_exit或者线程执行返回,正常退出时,析构函数就会被调用,异常退出不会调用析构函数. 3.线程退出时,线程私有数据的析构函数将按照OS实现定义的顺序被调用。析构函数可能调用另外一个函数,而该函数可能创建新的线程私有数据而且把这个线 程私有数据和当前的键关联起来。当所有的析构函数都调用完成以后,系统会检查是否有非NULL的线程私有数据值与键关联,如果有的话,再次调用析构函数, 这个过程一直重复到线程所有的键都为NULL值线程私有数据,或者已经做了PTHREAD_DESTRUCTOR_ITERATIONS中定义的最大次数 的尝试. TSD的读写都通过专门的Posix Thread函数进行,其API定义如下: int pthread_setspecific(pthread_key_t key, const void *pointer) void * pthread_getspecific(pthread_key_t key) 写入(pthread_setspecific())时,将pointer的值(不是所指的内容)与key相关联,而相应的读出函数则将与key相关联的数据读出来。数据类型都设为void *,因此可以指向任何类型的数据。 在LinuxThreads中,使用了一个位于线程描述结构(_pthread_descr_struct)中的二维void *指针数组来存放与key关联的数据,数组大小由以下几个宏来说明: #define PTHREAD_KEY_2NDLEVEL_SIZE 32 #define PTHREAD_KEY_1STLEVEL_SIZE ((PTHREAD_KEYS_MAX + PTHREAD_KEY_2NDLEVEL_SIZE - 1) / PTHREAD_KEY_2NDLEVEL_SIZE) 其中在/usr/include/bits/local_lim.h中定义了PTHREAD_KEYS_MAX为1024,因此一维数组大小为32。而具体存放的位置由key值经过以下计算得到: idx1st = key / PTHREAD_KEY_2NDLEVEL_SIZE idx2nd = key % PTHREAD_KEY_2NDLEVEL_SIZE 也就是说,数据存放与一个32×32的稀疏矩阵中。同样,访问的时候也由key值经过类似计算得到数据所在位置索引,再取出其中内容返回。 6.int pthread_delete(pthread_key_t *keyp),注意调用pthread_delete不会激活与键关联的析构函数,容易造成内存泄露。 当删除线程私有数据键的时候,不会影响任何线程对该键设置的线程私有数据值,甚至不影响调用线程当前键值,所以容易造成内存泄露,如果你不记得释放所有线程内与该键相关联的私有数据空间的话。 使用已经删除的私有数据键将导致未定义的行为。 编程建议:最后不删除线程私有数据键!!!尤其当一些线程仍然持有该键的值时,就更不该释放该键!!!

你可能感兴趣的:(线程的终止和私有数据)