当前位置:嗨网首页>书籍在线阅读

03-多线程

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

7.2 多线程

那么,为什么要有线程呢?显然,我们需要进程,因为它们是正在运行的程序的抽象。但是,为什么要分离执行单元,引入线程?多线程机制提供了六大好处:

编程抽象

把工作切分成多个模块,并为每个分块分配一个执行单元(线程)是解决很多问题的常见方式。利用这种方法的设计模式包括“每个连接一个线程”和线程池模式。编程人员觉得这些模式有用且直观。但是,有些人觉得线程破坏了模式理念。Alan Cox曾提出这样的说法——“线程是为那些不会使用状态机编程的人设计的”。也就是说,从理论上而言,所有可通过线程解决的编程问题都可以通过状态机解决。

并发性

对于有多个处理器的计算机,线程提供了一种实现“真正并发”的高效方式。每个线程有自己的虚拟处理器,是作为独立的调度实体,因此在多个处理器上可以同时运行多个线程,从而提高系统的吞吐量。由于线程可以实现并发性——也就是说,线程数小于等于处理器数——前面提到的“线程是为那些不会使用状态机编程的人设计的”的说法是不成立的。

提高响应能力

即使是在单处理器的计算机上,多线程也可以提高进程的响应能力。在单线程的进程中,一个长时间运行的任务会影响应用对用户输入的响应,导致应用看起来“僵死”了。有了多线程机制,这些操作可以委托给worker线程,至少有一个线程可以响应用户输入并执行UI操作。

I/O阻塞

这和前一项“提高响应能力”紧密相关。如果没有线程,I/O阻塞会影响整个进程。这对吞吐量和延迟都是灾难。在多线程的进程中,单个线程可能会因I/O等待而阻塞,而其他线程可以继续执行。除了线程之外,异步I/O和非阻塞I/O也是这种问题的解决方案。

上下文切换

在同一个进程中,从一个线程切换到另一个线程的代价要显著低于进程间的上下文切换。

内存保存

线程提供了一种可以共享内存,并同时利用多个执行单元的高效方式。从这个角度看,多线程在某些场景下可以取代多进程。

基于以上这些原因,多线程是操作系统以及应用的较常见的特性。在某些系统上,如Android,几乎每个进程都是多线程的。十来年前,“线程是为那些不会使用状态机编程的人设计的”的说法基本正确,因为线程的绝大多数优势都可以通过其他方式实现,比如非阻塞I/O以及状态机。现在,即使是在很小的计算机上都有多个处理器——甚至连手机都有多个处理器——而某些技术如多核和同步线程(SMT)更使得线程成为最大化系统吞吐量的工具。现在,难以想象会存在一个高性能的Web服务不是运行在多核的多线程上。

上下文切换:进程和线程 线程的一大性能优势在于同一个进程内的线程之间的上下文切换代价很低(进程内切换)。在任何系统上,进程内切换的代价低于进程间切换,前者通常是后者的一小部分。在非Linux系统上,这种代价差别非常明显,进程间通信代价非常高。因此,在很多系统上,称线程为“轻量级进程”。 在Linux中,进程间切换代价并不高,而进程内切换的成本接近于0:接近进入和退出内核的代价。进程的代价不高,但是线程的代价更低。 计算机体系结构对进程切换有影响,而线程不存在这个问题,因为进程切换涉及把一个虚拟地址空间切换到另一个虚拟地址空间。举个例子,在x86系统上,转换后备缓冲器(translation lookaside buffer, TLB),即用于把虚拟内存地址映射到物理内存地址的缓存,当切换到虚拟地址空间时,必须都清空。在某些负载场景下,TLB丢失对系统性能有极大损伤。在极端情况下,在某些ARM机器上,必须把整个CPU缓存都清空!对于线程而言,不存在这些代价,因为线程到线程之间的切换并不会交换虚拟地址空间。