线程概述
进程是系统中程序执行和资源分配的基本单位。每个进程有自己的数据段、代码段和堆栈段。
线程通常叫做轻型的进程。线程是在共享内存空间中并发执行的多道执行路径,他们共享一个进程的资源。
线程的共性
同一个进程中的线程都有共性:多个线程将共享同一个进程虚拟空间。
线程共享的环境包括:进程代码段、进程的公有数据(利用这些共享的数据,线程 很容易的实现相互之间的通讯)、进程打开的文件描述符、信号的处理器、进程的当前目录和进程用户ID与进程组ID。
线程的个性
线程ID–每个线程都有自己的线程ID,这个ID在本进程中是唯一的
寄存器组的值–一个线程切换到另一个线程上时,必须将原有的线程的寄存器集合的状态保存,以便将来该线程在被重新切换到时能得以恢复
线程的堆栈–线程必须拥有自己的函数堆栈,使得函数调用可以正常执行,不受其他线程的影响
错误返回码–不同线程拥有自己的错误返回码变量
线程的信号屏蔽码–线程的信号屏蔽码应该由线程自己管理,但所有的线程都共享同样的信号处理器
线程的优先级–由于线程需要像进程那样能够被调度,那么就必须要有可供调度使用的参数,这个参数就是线程的优先级
使用多线程的理由之一是和进程相比,它是一种非常 " 节俭 " 的多任务操作方式。
使用多线程的理由之二是线程间方便的通信机制。
使用多线程的优点:
提高应用程序响应。当一个操作耗时很长时,整个系统都会等待这个操作,多线程技术会将耗时长的操作( time cons uming )置于一个新的线程。
使多 CP U 系统更加有效。操作系统会保证当线程数不大于 CPU数目时,不同的线程运行于不同的 CP U 上。
改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分。
进程线程对比:
pthread 定义为 C 语言类型和过程 ( 函数 ) 集,包含在头文件pthread.h 和 pthread 库中。
大部分 pthreads 函数成功执行返回 0 ,出错返回非 0 值。
常见模型:
管理者/工作者:管理者 ( 单个线程 ) 分配任务给多个工作者( 多个线程 ) ,可采用工作池方式。
生产线:一任务分为许多顺序子任务,一个子任务完成后进入下一子任务。各个子任务采用线程完成。如同汽车生产线。
对等:类似于管理者 / 工作者,只是管理者分配完任务后也加入任务中去。
pthreads API 主要包含4部分的内容:
1. 线程管理:指直接使用线程的例程,如创建、分离、加入线程等,也包括设置/查询线程属性等例程。
2. 互斥:处理线程同步的例程。包括创建、 删除、 锁定、 解锁、互斥量等。
3. 条件变量:线程间通过共享互斥量通讯的例程。包括在指定的条件变量上创建、删除、发信号、等待等例程。
4. 同步化:管理读写锁和路障的例程。
命名规程
前缀 | 说明 |
pthread_ | 线程本身及各种子例程 |
pthread_attr_ | 线程属性对象 |
pthread_mutex_ | 互斥量 |
pthread_mutexattr_ | 互斥属性对象 |
pthread_cond_ | 条件变量 |
pthread_condattr_ | 条件属性对象 |
pthread_key_ | 线程特定的关键数据 |
pthread_rwlock_ | 读写锁 |
pthread_barrier_ | 同步化路障 |
线程函数:
创建和终止线程
int pthread _create(pthread _t * thread , const pthread_attr_t * attr , void *(*start_routine)(void *), void * arg );
void pthread_exit( void * status);
int pthread_cancel (pthread_t thread);
用 pthread_create() 来创建线程,在线程的处理函数中,可以显示的调用 pthread_exit()结束线程执行,也可以不调用pthread_exit() ,而只是让线程处理程序返回。线程也可以使用 pthread_cancel() 函数终止其他线程的执行。
结合和分离线程
int pthread_join( pthread_t threadid , void ** status );
int pthread_detach(pthread_t threadid);
等待线程结束
pthread_join()函数会挂起创建线程的线程的执行,直到等待到想要等待的子线程。
线程的分离
主线程创建子线程,当子线程本身自己有自我回收内存资源的能力时可以和主线程分离。
其它一些线程管理函数
pthread_t pthread_self (void);
int pthread_equal (pthread_t thread1, pthread_t thread2);
int pthread_once(pthread_once_t *once_control,void (*init_routine)(void));
用 pthread_self() 来获得当前线程的标识符。用 pthread_equal() 来判断两个进程标识符是否相等,相等返回 0 ,否则非 0 值。当线程初始化只需要执行一次时,使用 pthread_once()来控制初始化例程只被执行一次。
进程线程函数对比:
进程 | 线程 | 描述 |
fork | pthread_create | 创建新的控制流 |
exit | pthread_exit | 从控制流中退出 |
waitpid | pthread_join | 获取控制流退出状态 |
atexit | pthread_cancel_push | 注册控制流退出时要执行 的函数 |
getpid | pthread_self | 获取控制流 ID |
abort | pthread_cancel | 控制流非正常退出 |
Pthreads API-属性对象:
线程具有属性,用 pthread_attr_t 表示,通过设置属性,可以指定一种不同于缺省行为的行为。使用 pthread_create() 创建线程时,或初始化同步变量时,可以指定属性对象 ( 通常使用缺省值就足够了 )。在进程退出时需要销毁属性对象。属性对象是不透明的,而且不能通过赋值直接进行修改。系统提供了一组函数,用于初始化、配置和销毁每种对象类型。
初始化和配置属性后,属性便具有进程范围的作用域。使用属性时最好的方法即是在程序执行早期一次配置好所有必需的状态规范。然后,根据需要引用相应的属性对象。
使用属性对象具有两个主要优点。
1. 使用属性对象可增加代码可移植性。
2. 应用程序中的状态规范已被简化
int pthread_attr_init( pthread_attr_t *tattr);
int pthread_attr_destroy(pthread_attr_t *tattr) ;
int pthread_attr_s etdetachstate( pthread_attr_t *tattr,int detach state); // 设置分离状态
int pthread_attr_getdetachstate(const pthread_attr_t *tattr ,int *detach state); // 获取分离状态
P THREAD_CREATE_ DETACHE D
P THREAD_CREATE_JO I NABL E
int p thread_attr_setguard size(pthread_attr_t *attr ,size_t guard size); // 设置栈溢出保护区大小
int pthread_attr_getguardsize(const pthread_attr_t *attr ,size_t *guardsize); // 获取栈溢出保护区大小
int pthread_attr_setstacksize(pthread_attr_t *tattr ,size_t size); // 设置栈大小
int pthread_attr_getstacksize(pthread_attr_t *tattr ,size_t *size); // 获取栈大小
size 包含新线程使用的栈的字节数。如果 size 为零, 则使用缺省大小。在大多数情况下,零值最适合。 PTHREAD_STACK_MIN是启动线程所需的栈空间量。此栈空间没有考虑执行应用程序代码所需的线程例程要求。
同步对象是内存中的变量,可以按照与访问数据完全相同的方式对其进行访问。不同进程中的线程可以通过放在由线程控制的共享内存中的同步对象互相通信。尽管不同进程中的线程通常互不可见,但这些线程仍可以互相通信。
同步对象还可以放在文件中。同步对象可以比创建它的进程具有更长的生命周期。
同步对象具有以下可用类型:
互斥锁
条件变量
读写锁
信号