# 综述

# 线程的引入

  • 进程具有二个基本属性:
    一个拥有资源的独立单位:它可独立分配虚地址空间、主存和其它
    又是一个可独立调度和分派的基本单位
    这二个基本属性使进程成为并发执行的基本单位
    在一些早期的 OS 中,比如大多数 UNIX 系统、Linux 等,进程同时具有这二个属性。
  • 由于进程是一个资源的拥有者,因而在进程创建、撤销、调度切换时,系统需要付出较大的时空开销。
  • 进程的数目不宜过多,进程切换频率不宜过高,限制了并发程度
  • 操作系统的设计目标
    • 提高并发度
    • 减小系统开销
  • 将进程的两个基本属性分开,对于拥有资源的基本单位,不对其进行频繁切换,对于调度的基本单位,不作为拥有资源的单位,“轻装上阵”
  • 引入线程的目的是简化线程间的通信,以小的开销来提高进程内的并发程度。

# 线程概念

# 进程与线程

  • 进程:资源分配单位(存储器、文件)和 CPU 调度(分派)单位。又称为 "任务 (task)"
  • 线程:作为 CPU 调度单位,而进程只作为其他资源分配单位。
    • 只拥有必不可少的资源,如:线程状态、程序计数器、寄存器上下文和栈
    • 同样具有就绪、阻塞和执行三种基本状态
    • 与同属一个进程的其它线程共享进程拥有的全部资源
    • 可并发执行

# 线程的优点

  • 线程的优点:减小并发执行的时间和空间开销(线程的创建、退出和调度),因此容许在系统中建立更多的线程来提高并发程度。

    • 线程的创建时间比进程短;
    • 线程的终止时间比进程短;
    • 同进程内的线程切换时间比进程短;
    • 由于同进程内线程间共享内存和文件资源,可直接进行不通过内核的通信;
  • 线程(轻型进程)是 CPU 运用的一个基本单元,包括

    • 程序计数器
    • 寄存器集
    • 栈空间
  • 一个线程与它的对等线程共享:

    • 代码段
    • 数据段
    • 操作系统资源
      总体作为一个任务
  • 传统的或重型进程等价于只有一个线程的任务
    1

# 进程和线程的比较

  • 并发性:在引入线程的 OS 中,不仅进程之间可以并发执行,而且在一个进程中的多个线程之间亦可并发执行,因而使 OS 具有更好的并发性,从而能更有效地使用系统资源和提高系统吞吐量。
  • 拥有资源:进程是拥有资源的独立单位
  • 系统开销:在创建或撤消进程时,系统都要为之分配或回收资源,如内存空间、I/O 设备等。因此,OS 所付出的开销将明显地大于在创建或撤消线程时的开销。
  • 地址空间和其他资源(如打开文件):进程间相互独立,同一进程的各线程间共享--某进程内的线程在其他进程不可见
  • 通信:进程间通信 IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信--需要进程同步和互斥手段的辅助,以保证数据的一致性
  • 调度:线程上下文切换比进程上下文切换要快得多;

# 益处

  • Responsiveness
    响应度高:一个多线程的应用在执行中,即使其中的某个线程阻塞,其他的线程还可继续执行,从而提高响应速度
  • Resource Sharing
    资源共享:同一进程的多个线程共享该进程的内存等资源
  • Economy 经济性:创建和切换线程的开销要低于进程。比如,Solaris 中进程创建时间是线程创建的 30 倍,进程切换时间是线程切换的 5 倍。
  • U- tilization of MP Architectures
    MP 体系结构的运用:多线程更适用于多处理机结构。
  • Kernel-supported threads 内核支持的线程 (Mach and OS/2).
  • User-level threads; supported above the kernel, via a set of library calls at the user level (Project Andrew from CMU). 用户级线程;在内核之上,通过用户级的库调用
  • Hybrid approach implements both user-level and kernel-supported threads (Solaris 2). 混合处理实现用户级和内核支持线程

# 用户和内核线程

# 内核线程

  • Supported by the Kernel 由内核支持,在内核空间执行线程创建、调度和管理
    Thread is unit of CPU scheduling

  • 依赖于 OS 核心,由内核的内部需求进行创建和撤销,用来执行一个指定的函数。Windows NT 和 OS/2 支持内核线程;

  • 例子

    • Windows XP/2000
    • Solaris
    • Digital UNIX
  • 内核维护进程和线程的上下文信息;

  • 线程切换由内核完成;

  • 一个线程发起系统调用而阻塞,不会影响其他线程的运行。

  • 时间片分配给线程,所以多线程的进程获得更多 CPU 时间。

# 用户线程

  • 由用户级线程库进行管理的线程
  • 线程库提供对线程创建 \ 调度和管理的支持,无需内核支持。
  • 例子:
    • POSIX Pthreads
    • Mach C-threads
    • Solaris threads
  • 不依赖于 OS 核心,应用进程利用线程库提供创建、同步、调度和管理线程的函数来控制用户线程。调度由应用软件内部进行,通常采用非抢先式和更简单的规则,也无需用户态 / 核心态切换,所以速度特别快。
  • 用户线程的维护由应用进程完成;
  • 内核不了解用户线程的存在;
  • 用户线程切换不需要内核特权;
  • 用户线程调度算法可针对应用优化;
  • DRAWBACKS:
    如果内核是单线程的,那么一个用户线程发起系统调用而阻塞,则整个进程阻塞。
    时间片分配给进程,多线程则每个线程就慢。

# 用户线程与内核线程

  • 调度方式:内核线程的调度和切换与进程的调度和切换十分相似,用户线程的调度不需 OS 的支持。
  • 调度单位:用户线程的调度以进程为单位进行,在采用时间片轮转调度算法时,每个进程分配相同的时间片。对内核级线程,每个线程分配时间片。

# 多线程模型

指内核线程和用户线程之间

# 多对一

  • 多个用户级线程映像进单个内核线程
  • 任一时刻只能有一个线程可以访问内核 (并发性低)
  • 一个用户线程发起系统调用而阻塞,则整个进程阻塞。

# 一对一

  • 每个用户级线程映像进内核线程
  • Allowing another thread to run when a thread makes a blocking system call
  • 提供了更好的并发性,一个用户线程发起系统调用而阻塞时允许另一个线程运行
  • 每创建一个用户级线程需创建一个相应的内核线程,带来了额外开销,所以许多系统限制应用中的线程数目
  • 例如:Windows95/98/NT,OS/2

# 多对多

  • 多对一模型的缺点:不能实现真正的并发
  • 一对一模型的缺点:需限制应用中的线程数目
  • Many-to-many model multiplexes many user-level threads to a smaller or equal number of kernel threads.
  • 多对多模型:不限制应用的线程数、多个线程可以并发

# Two-level Model

  • Similar to M:M, except that it allows a user thread to be bound to kernel thread

# Threading Issues

  • Semantics 语义 of fork () and exec () system calls
  • Thread cancellation
  • Signal handling 信号处理
  • Thread pools 线程池
  • Thread specific data 线程特定数据

# fork and exec system calls

  • fork is used to create a separate, duplicate process
  • in multithreaded program, some UNIX have two versions of fork:
    • duplicate all threads in the process 复制所有线程
      • used when no following exec call
    • duplicate only the thread calling fork 只复制调用 fork 的线程
      • used when a immediately followed exec call
  • exec replaces the entire process, including all threads
    exec 参数所指定的程序会替换整个进程

# Two cancellation scenarios

  • target thread: the thread to be cancelled
  • asynchronous cancellation 异步取消: one thread immediately terminates the target thread 一个线程立即终止目标线程
    • may be cancelled in the middle of updating data shared with other threads
    • may not free a system-wide resource
  • deferred cancellation 延迟取消: the target thread can periodically check if it should terminate (at so called cancellation points in Pthread) 目标线程检查它是否应该终止

# signal delivery

  • 当特定事件发生时,一般会给进程发送信号来通知

  • 信号可以被同步或异步的接收

  • 同步信号会发送到产生信号的同一个进程。比如,非法内存访问,或者除零错。

  • 当信号是由运行进程之外的事件所产生 ,那么进程就异步的接收信号。比如,定时器中断或者 ctrl+C。

  • in single-thread programs, signals are always delivered to a process 单线程进程中,信号总是发送给进程

  • in multithreaded programs, where should a signal be delivered?
    在多线程进程中,信号被发送到哪里?

  • 同步信号需要发送到产生信号的线程,而不是进程中的其他线程

  • 异步信号??

    • the thread to which the signal applies 信号适用的线程
    • every thread in the process 进程中的所有线程
    • certain threads in the process 进程的某些线程
    • a specific thread to receive all signals 一个特定线程用来接收所有信号
  • Some asynchronous signals such as ctrl+C should be sent to all threads
    有些信号,如 ctrl+C 应该发送给所有的线程

  • Allow a thread to specify which signals it will accept and which it will block.
    允许线程描述它会接收的信号和拒绝的信号,则信号只发给那些不拒绝它的线程,由于信号只能被处理一次,一般将信号发给进程中不拒绝它的第一个线程

  • Allow a signal to be delivered to a specified thread
    允许信号发送给一个特定线程,该线程专门处理信号

# Thread pools

  • Why the thread pools? 为什么要线程池
    • avoid creation and termination overhead, so that it is faster to service a request 避免创建和撤销开销
    • put bound on number of threads, thus limit CPU and memory usage 限制线程的数量
  • Main idea of thread pools
    • 进程创建时就创建一定数量的线程,放入线程池中等待;
    • 进程收到请求时,唤醒线程池中的一个线程进行处理;
    • 线程完成工作,返回线程池中继续等待;
    • 如果没有可用的线程,进程就等待直到有空线程为止
  • 线程池限制了线程的数量
  • 用现有线程提供服务比创建线程快
  • 所有线程只创建和撤销一次,在线程池中复用,减小了开销
  • considerations
    • number of CPUs
    • amount of physical memory
    • expected number of concurrent requests

# Thread-Specific Data

  • threads belonging to a process share the data of that process
  • thread-specific data 线程特定数据 is a independent copy of certain data owned by one thread
  • example
    • in a transaction processing system, service each transaction in a separate thread
    • transaction ID to each thread
  • supported in most thread libraries including Win32, Pthreads, Java

# Solaris2 线程

  • Solaris 支持内核线程 (Kernel threads)、轻权进程 (Lightweight Processes) 和用户线程 (User Level Threads)。一个进程可有大量用户线程;大量用户线程复用少量的轻权进程,不同的轻权进程分别对应不同的内核线程。
  • Solaris 2 is a version of UNIX with support for threads at the kernel and user levels, symmetric multiprocessing, and real-time scheduling.
    Solaris 2 是 UNIX 的一个版本,支持内核级和用户级线程,对称多处理和实时调度
  • LWP – intermediate level between user-level threads and kernel-level threads.
    LWP 在用户级线程和内核级线程之间层次

# 轻权进程 (LightWeight Process)

  • 它是内核支持的用户线程,是内核数据结构,驻留在内核空间。一个进程可有一个或多个轻权进程,每个轻权进程由一个单独的内核线程来支持
  • 由内核调度程序进行调度
  • 允许一个进程中发出多个并发的系统调用 (因为有多个线程
  • 由线程库进行管理和调度

# 线程举例

  • 用户级线程在使用系统调用时(如文件读写),需要 “捆绑 (bound)” 在一个 LWP 上。
    • 永久捆绑:一个 LWP 固定被一个用户级线程占用,该 LWP 移到 LWP 池之外
    • 临时捆绑:从 LWP 池中临时分配一个未被占用的 LWP
  • 对于没有绑定的 LWP,则由线程库动态地进行调整:
    • 一个进程对应的 LWP 组成 LWP 池,线程库动态调整池中 LWP 的数目,以保证应用的最佳性能:
      • 当池中的 LWP 全部阻塞,而进程中还有线程可以运行,则线程库会为之创建另一个 LWP;
      • 当一个 LWP 长时间没用(老化,一般为 5 分钟),则线程库会删除它。
  • 在使用系统调用时,如果所有 LWP 已被其他用户级线程所占用(捆绑),则该线程阻塞直到有可用的 LWP--例如 6 个用户级线程,而 LWP 池有 4 个 LWP
  • 如果 LWP 执行系统调用时阻塞(如 read () 调用),则当前捆绑在 LWP 上的用户级线程也阻塞。

# WindowsNT

就绪状态 (Ready):进程已获得除处理机外的所需资源,等待执行。
备用状态 (Standby):特定处理器的执行对象,系统中每个处理器上只能有一个处于备用状态的线程。
运行状态 (Running):完成描述表切换,线程进入运行状态,直到内核抢先、时间片用完、线程终止或进行等待状态。
等待状态 (Waiting):线程等待对象句柄,以同步它的执行。等待结束时,根据优先级进入运行、就绪状态。
转换状态 (Transition):线程在准备执行而其内核堆栈处于外存时,线程进入转换状态;当其内核堆栈调回内存,线程进入就绪状态。
终止状态 (Terminated):线程执行完就进入终止状态;如执行体有一指向线程对象的指针,可将线程对象重新初始化,并再次使用。
初始化状态 (Initialized):线程创建过程中的线程状态;
2

# NT 线程有关 API

CreateThread () 函数在调用进程的地址空间上创建一个线程,以执行指定的函数;返回值为所创建线程的句柄。
ExitThread () 函数用于结束本线程。
SuspendThread () 函数用于挂起指定的线程。
ResumeThread () 函数递减指定线程的挂起计数,挂起计数为 0 时,线程恢复执行。

# Thread library

Thread library provides programmer with API for creating and managing threads
Two primary ways of implementing
Library entirely in user space
Kernel-level library supported by the OS
Examples:
POSIX Pthreads 支持用户级和内核级线程库
Win32 threads 支持内核线程
Java thread

# Java 线程

  • Java threads are managed by the JVM
  • Typically implemented using the threads model provided by underlying OS
    • On windows system, java threads are implemented using Win32 API
    • On UNIX or Linux, use Pthreads
  • Java Threads May be Created by: Java 线程可如下创建:
    • Extending Thread class
      扩充线程类
    • Implementing the Runnable interface
      实现可运行接口

# 线程类型的扩展

线程类型的拓展
1
2
3
4
5
6
7
8
class  Worker1 extends Thread
{
public void run() {
System.out.println(“I am a Worker Thread”);
}
}


# 创建线程

创建线程
1
2
3
4
5
6
7
8
9
10
public class First
{
public static void main(String args[]) {
Worker1 runner = new Worker1();

runner.start();

System.out.println(“I am the main thread”);
}
}

# 可运行接口

可运行接口
1
2
3
4
5
6
public interface Runnable
{
public abstract void run();
}
Runnable接口提供了run()方法,所以接下来要定义一个类实现Runnable接口的话,必须定义一个run()方法

# Interface 可运行接口的实现

Interface可运行接口的实现
1
2
3
4
5
6
7
8
class  Worker2 implements Runnable
{
public void run() {
System.out.println(“I am a Worker Thread”);
}
}

类Worker2实现了Runnable接口,并定义了run() 方法

# 创建 run 线程

创建run线程
1
2
3
4
5
6
7
8
9
10
11
public class Second
{
public static void main(String args[]) {
Runnable runner = new Worker2();
Thread thrd = new Thread(runner);
thrd.start();

System.out.println(“I am the main thread”);
}
} /*创建了一个线程对象传递给Runnable对象,当这个新的线程由start()方法创建后,新线程开始执行Runnable对象的run()方法*/

# Java 线程的管理

  • suspend () – suspends execution of the currently running thread. 挂起 - 暂停当前线程的运行
  • sleep() – puts the currently running thread to sleep for a specified amount of time.
    睡眠 - 让当前线程入睡一段指定的时间
  • resume() – resumes execution of a suspended thread.
    恢复 - 再执行被挂起的线程
  • stop() – stops execution of a thread.
    停止 - 停止一个线程的执行

# java 线程状态

3

# 生产者消费者问题

生产者消费者问题
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Server {  
public Server() {
MessageQueue mailBox = new MessageQueue();
Producer producerThread = new Producer(mailBox);
Consumer consumerThread = new Consumer(mailBox);

producerThread.start();
consumerThread.start();
}
public static void main(String args[]) {
Server server = new Server();
}
}

# 生产者线程

生产者线程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Producer extends Thread {
public Producer(MessageQueue m) {
mbox = m;
}
public void run() {
while (true) {
// produce an item & enter it into the buffer
Date message = new Date();
mbox.send(message);
}
}
private MessageQueue mbox;
}

# 消费者线程

消费者线程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Consumer extends Thread {
public Consumer(MessageQueue m) {
mbox = m;
}
public void run() {
while (true) {
Date message = (Date)mbox.receive();
if (message != null)
// consume the message
}
}
private MessageQueue mbox;
}



更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

小春日和 微信支付

微信支付

小春日和 支付宝

支付宝

小春日和 wechat

wechat