线程与传统进程的比较

线程与进程的比较

  • 调度:线程作为调度的基本单位,同进程中线程切换不引起进程切换,当不同进程的线程切换才引起进程切换;进程作为拥有资源的基本单位。
  • 并发性:一个进程间的多个线程可并发。
  • 拥有资源:线程仅拥有隶属进程的资源;进程是拥有资源的独立单位。
  • 系统开销:进程大;线程小。

操作系统  线程概念2_操作系统

线程间的同步和通信

互斥锁(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
  • 其切换也与进程的调度的切换十分相似,也分抢占式与非抢占式两种。在线程的调度算法上也可以采用时间片轮转法、优先权法等
    操作系统  线程概念2_操作系统_02

用户级线程的实现

  • 运行时系统(Runtime System)
    所谓“运行时系统”,实质上是用于管理和控制线程的函数(过程)的集合, 其中包括

    • 用于创建和撤消线程的函数、 线程同步和通信的函数以及实现线程调度的函数等
    • 这些函数使用户级线程与内核无关
    • 运行时系统中的所有函数都驻留在用户空间,并作为用户级线程与内核之间的接口
  • 内核控制线程
    这种线程又称为轻型进程LWP(Light Weight Process)。

    • 每一个进程都可拥有多个LWP, 同用户级线程一样, 每个LWP都有自己的数据结构(如TCB),其中包括线程标识符、优先级、 状态, 另外还有栈和局部存储区等
    • 也可以共享进程所拥有的资源。LWP可通过系统调用来获得内核提供的服务,这样,当一个用户级线程运行时,只要将它连接到一个LWP上,此时它便具有了内核支持线程的所有属性

操作系统  线程概念2_操作系统_03

Linux线程的基本函数

常用线程函数

  • pthread_create 创建一个线程
  • pthread_exit 线程自行退出
  • pthread_join 其它线程等待某一个线程退出
  • pthread_cancel 其它线程强行杀死某一个线程
  • pthread线程库的使用
  • 在源码中使用头文件 pthread.h
  • 用gcc链接时加上 -lpthread 选项,链接线程库