线程的基本概念和操作

系统为每个用户进程创建一个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。

  pthread_cond_signal函数的作用是发送一个信号给另外一个正在处于阻塞等待状态的线程,使其脱离阻塞状态,继续执行.如果没有线程处在阻塞等待状态,pthread_cond_signal也会成功返回。
  使用pthread_cond_signal一般不会有“惊群现象”产生,他最多只给一个线程发信号。假如有多个线程正在阻塞等待着这个条件变量的话,那么是根据各等待线程优先级的高低确定哪个线程接收到信号开始继续执行。如果各线程优先级相同,则根据等待时间的长短来确定哪个线程获得信号。但无论如何一个pthread_cond_signal调用最多发信一次。
  但是pthread_cond_signal在多处理器上可能同时唤醒多个线程,当你只能让一个线程处理某个任务时,其它被唤醒的线程就需要继续 wait,而且规范要求pthread_cond_signal至少唤醒一个pthread_cond_wait上的线程,其实有些实现为了简单在单处理器上也会唤醒多个线程. 
   另外,某些应用,如线程池,pthread_cond_broadcast唤醒全部线程,但我们通常只需要一部分线程去做执行任务,所以其它的线程需要继续wait.所以强烈推荐对pthread_cond_wait() 使用while循环来做条件判断.
 
还可以很好的控制条件来控制

 

 

 

 

 

 

 

posted on 2012-07-04 21:46  孟浩依然  阅读(434)  评论(0编辑  收藏  举报