11.2线程概念
线程包含了表示进程内执行环境的必须信息,其中包含进程中表示线程的线程ID,一组寄存器值,栈,调度优先级和策略。信号屏蔽字,errno值以及线程私有数据
进程的所有信息对该进程的所有线程都是共享的,包括可执行的程序文本,程序的全局内存和堆内存,栈以及文件描述符。
11.3线程标识
就像每一个进程都有一个进程ID一样,每一个线程也有一个线程ID,进程ID在整个系统中是唯一的,但线程ID不同,线程ID只在它所属的进程环境中有效。线程ID用pthread_t数据类型来表示,(Linux使用无符号长整数表示pthread_t结构)。实现的时候可以用一个结构来代表pthread_t数据类型,所以可移植的系统不能把它当做整数来处理,因此必须使用函数来对两个线程ID进行比较
#include<pthread.h>
int pthread_equal(pthread_t tid1,pthread_t tid2);//返回0表示相等
线程可以通过pthread_self函数获取自身的线程ID
#include<pthread.h>
pthread_t pthread_self(void)
//返回线程的线程ID
11.4线程创建
在传统unix模型中,每个进程只有一个控制线程,它也是以单个控制线程启动的。新增的线程可以通过pthread_create函数创建
#include<pthread.h>
pthread_t pthread_create(pthread_t *tidp,const pthread_attr_t *attr,void *(*start_rtn)(void),void * arg)//若成功返回0,否则返回错误编号
当pthread_create成功返回时,由tidp指向的内存单元被设置为新创建线程的线程ID,attr参数用于定制不同的线程属性。
新创建的线程将从start_rtn函数的地址开始运行,该函数只有一个无类型的参数arg,如果要向start_rtn函数传递的参数不止一个,那么需要把参数房贷一个结构中,然后把这个结构的地址当做arg参数传入。
线程创建时不能保证哪一个线程先运行,可以是新线程也可以是调用线程,新创建的线程可以访问进程的地址空间,并且继承调用线程的浮点环境和信号屏蔽字,但是该线程的未决信号集被清除。
注意pthread_create函数在调用失败是通常会返回错误码,他们并不像其他函数一样设置errno。每个线程提供一个errno副本,只是为了与使用errno的现有函数兼容。
11.5线程终止
如果进程中某一个线程调用了exit,_exit或者_Exit,那么整个进程就会终止。类似的,如果信号的默认动作是终止进程,那么把该信号发送到某个线程,整个进程都会终止。
单个线程可以有三种方式退出:
1.线程只是从启动例程中返回,返回值是线程退出码
2.线程可以被统一进程中的线程取消
3.线程调用pthread_exit
#include<pthread.h>
void pthread_exit(void *rval_ptr)
rval_ptr是一个无类型指针,进程中的其他线程可以通过调用pthread_join函数访问到这个指针
#include<pthread.h>
int pthread_join(pthread_t thread,void ** rval_ptr)
//成功返回0,否则返回错误码
调用线程一直阻塞,直到等待线程调用pthread_exit,从启动例程中返回或被取消。
如果只是从他的启动例程中返回,那么rval_ptr将包含返回码,如果线程被取消,由rval_ptr指向的内存单元就置为PTHREAD_CANCLE。
如果线程已经处于分离状态,那么,pthread_join调用就会失败。
pthread_create和pthread_exit函数的无类型指针参数能传递的数值不止一个,该指针可以传递更复杂信息的结构地址,但是注意这个结构所使用的内存,在调用者完成调用以后必须仍然是有效的,否则就会出现无效或者非法内存访问。例如在调用线程的栈上分配了该结构,那么其他线程在使用这个结构时内存可能就已经改变了。(可以使用全局栈结构malloc调用分配结构)。
线程可以调用pthread_cancle函数来请求取消统一进程中的其他线程。
#inlcude<pthread.h>
int pthread_cancle(pthread_t tid);
//返回值为0成功,否则返回错误编号
在默认情况下,pthread_cancle函数会使由tid标识的线程行为如同调用了参数为PTHREAD_CANCLE的pthread_exit函数,注意pthread_cancle并等待线程终止,它仅仅提出请求。
线程可以安排退出时它调用的函数,这样的函数成为线程清理处理程序。处理程序记录在栈中,也会就是说他们的执行顺序与注册时相反。
#inlcude<pthread.h>
void pthread_cleanup_push(void* (*rtn)(void*),void *arg);
void pthread_cleanup_pop(int excute)
线程清理函数会被调用时的情况:
调用pthread_exit时
响应取消请求时
用非0excute参数调用pthread_cleanup_pop函数时
如果excute参数为0时,清理函数将不会调用(如果相处通过它的启动例程返回的话那么就不会调用清理函数)
在默认情况下,线程的终止状态会保存到对该线程调用pthread_exit,如果线程已经处于分离状态,线程的底层资源可以在线程终止时立即收回。phtread_detach函数用于使线程进入分离状态
#inlcude<pthread.h>
int pthread_detach(pthread_t tid);
//若成功返回0,否则返回错误代码
11.6线程同步
会在UNIX网络编程中介绍线程的同步