线程与传统进程的比较
线程与进程的比较
- 调度:线程作为调度的基本单位,同进程中线程切换不引起进程切换,当不同进程的线程切换才引起进程切换;进程作为拥有资源的基本单位。
- 并发性:一个进程间的多个线程可并发。
- 拥有资源:线程仅拥有隶属进程的资源;进程是拥有资源的独立单位。
- 系统开销:进程大;线程小。
线程间的同步和通信
互斥锁(mutex)
互斥锁是一种比较简单的、用于实现进程间对资源互斥访问的机制。由于操作互斥锁的时间和空间开锁都较低, 因而较适合于高频度使用的关键共享数据和程序段
互斥锁可以有两种状态, 即开锁(unlock)和关锁(lock)状态。 相应地,可用两条命令(函数)对互斥锁进行操作。其中的关锁lock操作用于将mutex关上,开锁操作unlock则用于打开mutex
读写锁允许对受保护的共享资源进行并发读取和独占写入。若要修改资源,线程必须首先获取互斥写锁;只有释放了所有的读锁之后,才允许使用互斥写锁。
条件变量
- 每一个条件变量通常都与一个互斥锁一起使用,亦即,在创建一个互斥锁时便联系着一个条件变量。单纯的互斥锁用于短期锁定,主要是用来保证对临界区的互斥进入。而条件变量则用于线程的长期等待, 直至所等待的资源成为可用的
- 线程首先对mutex执行关锁操作,若成功便进入临界区,然后查找用于描述资源状态的数据结构,以了解资源的情况。 只要发现所需资源R正处于忙碌状态,线程便转为等待状态, 并对mutex执行开锁操作后,等待该资源被释放; 若资源处于空闲状态,表明线程可以使用该资源,于是将该资源设置为忙碌状态,再对mutex执行开锁操作
信号量机制
私用信号量(private samephore)
- 线程可利用私用信号量来实现同一进程中各线程之间的同步
- 调用创建信号量命令来创建一私用信号量
- 数据结构是存放在应用程序的地址空间中
- 私用信号量属于特定的进程所有,OS不知道私用信号量的存在
- 缺点:易丢失空间,私用信号量的占用者异常结束或正常结束,并未- 释放该信号量所占有空间时,系统将无法使它恢复为0(空); 也不能将它传送给下一个请求它的线程
公用信号量(public semaphore)
- 实现不同进程间或不同进程中各线程之间的同步
- 有一个公开的名字供所有的进程使用
- 数据结构是存放在受保护的系统存储区中,由OS为它分配空间并进行管理,故也称为系统信号量
- 优点:信号量的占有者在结束时未释放该公用信号量,则OS会自动将该信号量空间回收,并通知下一进程。可见,公用信号量是一种比较安全的同步机制
内核支持线程和用户级线程
内核支持线程(Kernel-Support Threads)
- 用户进程和系统进程都是在操作系统内核的支持下运行的,与内核紧密相关
- 内核支持线程由操作系统直接支持,内核在其空间内执行线程的创建、调度和管理。
- 所有线程管理由核心完成
- 没有线程库,但对核心线程工具提供API(应用编程接口)
- 核心维护进程和线程的上下文
- 线程之间的切换需要核心支持
- 进行调度以线程为基础
例子:Windows NT,OS/2
核心级线程的优点和缺点
- 优点:
- 对多处理器,核心可以同时调度同一进程的多个线程
- 阻塞是在线程一级完成
- 核心例程是多线程的
- 缺点:
- 在同一进程内的线程切换调用内核,导致速度下降
用户级线程(User-Level Threads)
- 用户级线程在内核之上支持,并在用户层通过线程库来实现。线程库提供对线程创建、调度和管理的支持而无需内核支持。
- 调度仍然是以进程为单位的
- 因内核并不知道用户级线程,所以所有线程的创建和调度是在用户空间内进行的,无需内核干涉
- 由于切换的规则远比进程调度和切换的规则简单,因而使线程的切换速度特别快。可见,这种线程是与内核无关的
用户级线程的优点和缺点
- 优点:
- 线程切换不调用核心
- 调度是应用程序特定的:可以选择最好的算法
- ULT可运行在任何操作系统上(只需要线程库)
- 缺点:
- 大多数系统调用是阻塞的,若核心阻塞进程,则进程中所有线程将被阻塞
- 核心只将处理器分配给进程,同一进程中的两个线程不能同时运行于两个处理器上
内核支持线程的实现
- 不论是进程还是线程都必须直接或间接地取得内核的支持。由于内核支持线程可以直接利用系统调用为它服务,故线程的控制简单
- 系统在创建一个新进程时,为它分配任务数据区PTDA(Per Task Data Area),其中包括若干个线程控制块TCB空间。在每个TCB中可保存线程标识符、优先级、线程运行的CPU状态等信息
- 进程在创建一个线程时,为它分配一个TCB,将有关信息写入该TCB中,并为之分配必要的资源
- 撤消线程时,也要回收该线程所有资源和TCB
- 其切换也与进程的调度的切换十分相似,也分抢占式与非抢占式两种。在线程的调度算法上也可以采用时间片轮转法、优先权法等
用户级线程的实现
运行时系统(Runtime System)
所谓“运行时系统”,实质上是用于管理和控制线程的函数(过程)的集合, 其中包括- 用于创建和撤消线程的函数、 线程同步和通信的函数以及实现线程调度的函数等
- 这些函数使用户级线程与内核无关
- 运行时系统中的所有函数都驻留在用户空间,并作为用户级线程与内核之间的接口
内核控制线程
这种线程又称为轻型进程LWP(Light Weight Process)。- 每一个进程都可拥有多个LWP, 同用户级线程一样, 每个LWP都有自己的数据结构(如TCB),其中包括线程标识符、优先级、 状态, 另外还有栈和局部存储区等
- 也可以共享进程所拥有的资源。LWP可通过系统调用来获得内核提供的服务,这样,当一个用户级线程运行时,只要将它连接到一个LWP上,此时它便具有了内核支持线程的所有属性
Linux线程的基本函数
常用线程函数
- pthread_create 创建一个线程
- pthread_exit 线程自行退出
- pthread_join 其它线程等待某一个线程退出
- pthread_cancel 其它线程强行杀死某一个线程
- pthread线程库的使用
- 在源码中使用头文件 pthread.h
- 用gcc链接时加上 -lpthread 选项,链接线程库