线程的基本概念和操作
系统为每个用户进程创建一个task_struct 来描述该进程,该结构体包含了一个指针指向该进程的虚拟地址空间映射表。实际上task_struct和地址空间映射表一起用来表示一个进程。
由于进程的地址空间是私有的,因此在进程间上下文切换时,系统开销比较大,为了提高系统的性能,许多操作系统规范里引入了轻量级进程的概念,即线程
在同一个进程中创建的线程共享该进程的地址空间,Linux里同样用sask_struct来描述一个线程,线程和进程都参与统一的系统调度。通常线程是指共享相同地址空间的多个任务。
使用线程的优势:
大大提高了任务切换的效率,避免了额外的TLB&cache的刷新
多线程通过第三方的线程库来实现:
New POSIX Thread Library(NPTL)
是早期Linux Threads的改进,采用的1:1线程模型,显著提高了运行效率,信号处理效率更高。
一个进程中的多个线程共享以下资源:
1.可执行的指令
2.静态数据
3.进程中打开的文件描述符
4.信号处理函数
5.当前工作目录
6.用户ID和用户组ID
另外,每个进程都会有私有的资源:线程的ID,PC(程序计数器)和相关的寄存器,堆栈,错误号,信号掩码和优先级,执行状态和属性
NPTL线程库提供了创建、删除、控制线程的几个基本操作。既然多个线程共享一个进程资源,那么就要考虑到资源共享时候的互斥以及进程间的同步了。
线程间的同步和互斥机制采用:信号量,互斥锁或者是条件变量等。
创建进程函数:
所需头文件: #include<pthread.h>
函数原型:int pthread_create(pthread_t %thread, const pthread_attr_t *attr,void *(*routine)(void *),void *arg)
函数参数:thread 创建的线程
attr 线程属性,NULL表示使用缺省属性
routine 线程执行的函数
arg 传递给线程执行的函数的参数
返回值: 成功 0,失败 -1;
线程等待函数:
所需头文件:#include <pthread.h>
函数原型为 :int pthread_join(pthread_t thread, void **retval);
第一个参数为被等待的线程标识符;
第二个参数为一个用户定义的指针,它可以用来存储被等待线程的返回值。
这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。
如果执行成功,将返回0,如果失败则返回一个错误号。
在Linux中,默认情况下是在一个线程被创建后,必须使用此函数对创建的线程进行资源回收,但是可以设置Threads attributes来设置当一个线程结束时,
直接回收此线程所占用的系统资源,详细资料查看Threads attributes。
其实在Linux中,新建的线程并不是在原先的进程中,而是系统通过一个系统调用clone()。该系统copy了一个和原先进程完全一样的进程,
并在这个进程中执行线程函数。不过这个copy过程和fork不一样。
copy后的进程和原先的进程共享了所有的变量,运行环境。这样,原先进程中的变量变动在copy后的进程中便能体现出来。
代码中如果没有pthread_join主线程会很快结束从而使整个进程结束,从而使创建的线程没有机会开始执行就结束了。加入pthread_join后,
主线程会一直等待直到等待的线程结束自己才结束,使创建的线程有机会执行。
所有线程都有一个线程号,也就是Thread ID。其类型为pthread_t。通过调用pthread_self()函数可以获得自身的线程号。
线程退出函数:
函数原型:int pthread_exit(void * value_ptr);
函数参数: value_ptr 保存线程退出时的返回的值
线程通过调用pthread_exit函数终止执行,就如同进程在结束时调用exit函数一样。
返回值:成功 0,失败 -1;
发送线程取消函数:
int pthread_cancel(pthread_t thread)
发送终止信号给thread线程,如果成功则返回0,否则为非0值。发送成功并不意味着thread会终止。
int pthread_setcancelstate(int state, int *oldstate)
设置本线程对Cancel信号的反应,state有两种值:PTHREAD_CANCEL_ENABLE(缺省)和PTHREAD_CANCEL_DISABLE,
分别表示收到信号后设为CANCLED状态和忽略CANCEL信号继续运行;old_state如果不为NULL则存入原来的Cancel状态以便恢复。
int pthread_setcanceltype(int type, int *oldtype)
设置本线程取消动作的执行时机,type由两种取值:PTHREAD_CANCEL_DEFFERED和PTHREAD_CANCEL_ASYCHRONOUS,
仅当Cancel状态为Enable时有效,分别表示收到信号后继续运行至下一个取消点再退出和立即执行取消动作(退出);oldtype如果不为NULL则存入运来的取消动作类型值。
void pthread_testcancel(void)
检查本线程是否处于Canceld状态,如果是,则进行取消动作,否则直接返回
因为多线程是基于第三方的线程库来实现,所以在编译的过程中要将库加载链接,例如:
#gcc -o example example.c -lpthread -D_REENTRANT
-lpthread :链接pthread库
-D_REENTRANT : 生成可重入代码
pthread_cond_wait()
pthread_cond_wait() 用于阻塞当前线程,等待别的线程使用pthread_cond_signal()或pthread_cond_broadcast来唤醒它。 pthread_cond_wait() 必须与pthread_mutex 配套使用。pthread_cond_wait()函数一进入wait状态就会自动release mutex。当其他线程通过pthread_cond_signal()或pthread_cond_broadcast,把该线程唤醒,使pthread_cond_wait()通过(返回)时,该线程又自动获得该mutex。