Untitled

概述

每个线程是 CPU 使用的一个基本单元,与同一线程的其它线程共享代码段、数据段和其他操作系统资源。

Untitled

  • 多线程编程具有如下四大类的优点:
    • 响应性:一个采用多线程的交互程序即使部分阻塞或者执行冗长操作,仍可以继续执行,从而增加对用户的相应程度
    • 资源共享:线程默认共享它们所属进程的内存和资源
    • 经济:进程创建所需的内存和资源更昂贵,创建和切换线程更加经济
    • 可伸缩性:线程可在多处理器核上并行运行

多核编程

并发性(concurrency):并发系统允许支持多个任务,允许所有任务都能取得进展(单核通过切换任务也能完成)

并行性(parallelism):并行系统可以同时执行多个任务

并发不一定并行,并行一定并发

Untitled

多线程模型

用户线程:通过用户层面的线程库实现线程管理,位于内核之上,无需内核支持,缺点是内核享受不到多线程带来的好处

内核线程:由操作系统来直接支持和管理

多对一模型

映射多个用户级线程到一个内核线程

Untitled

一对一模型

映射每个用户线程到一个内核线程、

优点是在一个线程执行阻塞系统调用时,能够允许另一个线程继续执行,所以提供了比多对一模型更好的并发功能;也允许多个线程并行运行在多处理器系统上。

缺点是创建一个用户线程就要创建一个相应的内核线程,而创建内核线程的开销会影响应用程序的性能,所以这种模型的大多数实现限制系统支持的线程数量。

Untitled

多对多模型

多路复用多个用户级线程到同样数量或更少数量的内核线程

Untitled

线程库

线程库为程序员提供创建和管理线程的 API

  • 实现线程库的主要方法有两种:
    • 在用户空间中提供一个没有内核支持的库,库的所有代码和数据结构都位于用户空间
    • 实现由操作系统直接支持的内核级的一个库,调用一个 API 函数通常导致对内核的系统调用

POSIX Pthreads、Windows 线程、Java 线程

多线程问题

系统调用fork()exec()

  • 线程调用fork(),有两种形式:
    • 新进程复制所有线程
    • 新进程只有单个线程,仅仅复制调用了fork()的线程

如果一个线程调用exec()系统调用,exec()参数指定的程序将会取代整个进程,包括所有线程。

如果分叉后立即调用exec(),则没有必要复制所有线程,只需复制调用线程。

信号处理

UNIX 信号用于通知进程某个特定事件已经发生

信号的接收可以是同步或异步的,取决于事件信号的来源和原因。

  • 所有信号遵循相同的模式:
    • 信号是由特定事件的发生而产生的
    • 信号被传递给某个进程
    • 信号一旦收到就应处理
  • 信号处理程序可以分为两种:
    • 缺省的信号处理程序
    • 用户定义的信号处理程序
  • 信号传递通常具有如下选择:
    • 传递信号到信号所适用的线程
    • 传递信号到进程内的每个线程
    • 传递信号到进程内的某些线程
    • 规定一个特定线程以接收进程的所有信号

线程撤销

线程撤销(Thread Cancellation)是在线程完成之前终止进程

  • 目标线程的撤销可以有两种情况:
    • 异步撤销:一个线程立即终止目标线程
    • 延迟撤销:目标线程不断检查它是否应终止,这允许目标线程有机会有序终止自己

线程池

在进程开始时创建一定数量的线程,并加到池中以等待工作

  • 使用线程池的优点:
    • 用现有线程服务请求比等待创建一个线程更快
    • 线程池限制了任何时候可用线程的数量

线程本地存储

除了共享进程的数据,每个线程可以拥有自己的数据,称为线程本地存储(Thread-Local Storage, TLS)