多线程

软件或硬件上实现多个线程并发执行的技术

多线程

多线程(multithreading)是指从软件或者硬件上实现多个线程并发执行的技术。具备多线程处理能力的计算机能够在硬件支持下同时执行多个线程,进而提升整体处理性能。[4]多线程技术自1950年代起,经历了从早期NBS SEAC和DYSEAC的双线程系统,到Lincoln Labs TX-2支持33个线程的演变。1960年代,CDC 6600和IBM ACS-360等系统通过引入多线程提升了硬件资源利用率。1970年代与1980年代,HEP、Xerox Alto、Transputer等项目进一步扩展了多线程的应用。1990年代,Tera MTA和华盛顿大学的SMT研究带来了技术突破。2000年代,Cray/Tera MTA-2和Intel Pentium 4 HT的推出,以及超线程技术的商业化,标志着多线程技术的成熟。[5]多线程技术通过细粒度、粗粒度和同步多线程等方式,[0][1][2]利用时间分片机制实现单个程序内多个线程的并行运行,每个线程按照分配的时间片轮流使用CPU,实现任务的并行处理。[3]

多线程(multithreading)是指从软件或者硬件上实现多个线程并发执行的技术。具备多线程处理能力的计算机能够在硬件支持下同时执行多个线程,进而提升整体处理性能。[5]

多线程技术自1950年代起,经历了从早期NBS SEAC和DYSEAC的双线程系统,到Lincoln Labs TX-2支持33个线程的演变。1960年代,CDC 6600和IBM ACS-360等系统通过引入多线程提升了硬件资源利用率。1970年代与1980年代,HEP、Xerox Alto、Transputer等项目进一步扩展了多线程的应用。1990年代,Tera MTA和华盛顿大学的SMT研究带来了技术突破。2000年代,Cray/Tera MTA-2和Intel Pentium 4 HT的推出,以及超线程技术的商业化,标志着多线程技术的成熟。[6]多线程技术通过细粒度、粗粒度和同步多线程等方式,[1][2][3]利用时间分片机制实现单个程序内多个线程的并行运行,每个线程按照分配的时间片轮流使用CPU,实现任务的并行处理。[4]

多线程通过后台处理耗时任务、增强界面互动和资源利用,提升了程序效率和用户体验,尤其在iOS开发中效果显著。然而,它也带来了性能、内存、稳定性和死锁等挑战。[7]

线程

线程(thread)是操作系统进行运算调度的最小单位,它存在于进程之中并作为进程中的实际运作单位。一个线程代表进程中的一个单一顺序控制流,允许一个进程中并发执行多个线程,每个线程可以并行执行不同的任务。在Unix System V和SunOS操作系统中,线程有时也被称为轻量进程(lightweight processes),但这个术语更多指的是内核线程(kernel thread),而用户线程(user thread)则通常被称为线程。[8]

线程出现的原因

自20世纪60年代起,进程作为操作系统中能独立运行的基本单位,因其拥有独立资源而在创建、撤销、调度切换以及同步与通信等方面需要系统付出较大的时空开销。此外,进程切换的频率也不宜过高,这限制了其并发程度的进一步提高。到了20世纪80年代中期,为了提升系统内程序的并发执行程度和吞吐量,提出了线程概念,这是一种比进程更小的能独立运行的基本单位。[9]

多线程的发展

自1950年代起,早期双线程系统如NBS SEAC(1950年)和DYSEAC(1954年)的出现标志着多线程技术的萌芽,这些系统开启了基本的并行处理能力。到了1950年代末,Lincoln Labs TX-2等系统已能支持多达33个线程,而Bull Gamma 60和Honeywell 800也在探索多线程技术。[6]

1960年代,CDC 6600和IBM ACS-360等系统进一步推动了多线程技术的发展,特别是IBM ACS-360通过引入第二套指令计数器和寄存器集,实现了更高效的硬件资源利用。1970年代和1980年代,HEP、Xerox Alto、HEP-2、HEP-3、Transputer和Horizon等项目不断推动多线程技术的发展,Stellar GS-1000更是实现了四路多线程。[6]

1990年代,Burton Smith的Tera MTA和华盛顿大学同步多线程(SMT)研究,尤其是Dean Tullsen、Susan Eggers和Henry Levy等人的工作,显著推进了SMT技术的发展。2000年代,Cray/Tera MTA-2和Intel Pentium 4 HT的推出,以及Intel Tech Journal关于超线程技术的特刊发布,标志着多线程技术进入了新的阶段。[6]

多线程是为了同步完成多项任务,其最终目标是提高计算机的计算速度,从而提高其性能。[10][11]

具有两个执行线程的进程
具有两个执行线程的进程

技术原理

多线程是一种并发执行机制,它允许多个线程在同一时间内并行处理。该机制的原理基于时间分片(time slicing),即将处理器的时间划分为多个短的时间片。每个时间片被分配给不同的线程,使得它们能够依次轮流执行。由于每个时间片非常短,对于任何一个应用程序而言,它都能获得连续的处理器时间,达到一种多个应用程序同时运行的效果。[4][12]

多线程就是把操作系统中的这种并发执行机制原理运用在一个程序中,把一个程序划分为若干个子任务,多个子任务并发执行,每一个任务就是一个线程。这就是多线程程序。[4]

技术实现

线程调度是操作系统中一个重要的功能,它负责决定哪个线程将获得CPU时间以执行。在大多数研究领域,线程调度器需要能够快速选择一个已就绪的线程去运行,以提高效率,而不是逐一运行每个线程。因此,调度器能够识别线程的优先级变得非常重要。线程调度器可以以硬件、软件或软硬件结合的形式实现。[13]另一个研究领域关注的是哪些事件会导致线程切换,例如缓存失败、内部运行续连系、使用直接内存访问(DMA)等会造成线程切换。[14][13]

在多线程方案中,如果复制了所有软件可见的状态,包括特许的控制登录、转换后备缓冲器(TLB)等,就可以让虚拟机创建多种线程。这样,每个线程可以在相同的处理器上运行各自的操作系统。换句话说,如果只存储了用户模式的状态,就可以让相同裸晶大小的芯片在一定时间内处理更多的线程。[13][15]

细粒度多线程

细粒度多线程(fine-grained multithreading)在每条指令执行后进行线程切换,实现多个线程间的交叉执行。这种执行模式通常采用轮转(round-robin)方式,每个时钟周期都会跳过那些处于阻塞状态的线程。为了实现细粒度多线程,处理器必须能够在每个时钟周期进行线程切换。这种技术的主要优点在于能够同时隐藏由短阻塞和长阻塞引起的吞吐量损失,因为当一个线程阻塞时,可以转而执行其他线程的指令。然而,细粒度多线程的主要缺点是可能会降低单个线程的执行速度,因为即使处于就绪状态且没有阻塞的线程也可能因为频繁的线程切换而延迟执行。[16]

粗粒度多线程

粗粒度多线程(coarse-grained multithreading)是与细粒度多线程相对应的一种线程执行策略。它仅在遇到高开销阻塞,如最后一级缓存缺失时,才进行线程切换,从而降低了对高速线程切换的需求。这种策略几乎不影响单个线程的执行速度,因为它只在当前线程遇到高开销阻塞时才会开始执行其他线程的指令。然而,粗粒度多线程在隐藏吞吐量损失方面的能力有限,特别是对于短阻塞,这是因为粗粒度多线程中的流水线启动开销较大。在粗粒度多线程处理器中,由于指令是从单一线程发射的,因此在阻塞发生时必须清空或冻结流水线。阻塞解除后,新线程必须在导致阻塞的指令完成前填充流水线。由于启动开销,粗粒度多线程更适合用来降低高开销阻塞带来的性能损失,因为在这种情况下,与阻塞时间相比,流水线重新填充时间是可以忽略的。[17]

同步多线程

同步多线程(Simultaneous Multi-Threading,简称SMT)是一种硬件多线程技术,它允许一个中央处理器(CPU)在每个时钟周期内同时执行来自多个线程的指令。这种技术在操作系统层面表现为软件多线程。本质上,SMT技术将多CPU的线程级并行处理能力转化为单个CPU的指令级并行处理能力。[3]

优点

使用线程可以把占据时间长的程序中的任务放到后台去处理。[7]用户界面可以更加吸引人,如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度。[7]程序的运行速度可能加快。[7]在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下可以释放一些珍贵的资源如内存占用等。[7]多线程技术在IOS软件开发中也有举足轻重的作用。[7]

缺点

如果有大量的线程,会影响性能,因为操作系统需要在它们之间切换。[7]更多的线程需要更多的内存空间。[7]线程可能会给程序带来更多“bug”,因此需谨慎使用。[7]线程的中止需要考虑其对程序运行的影响。[7]通常块模型数据是在多个线程间共享的,需要防止线程死锁情况的发生。[7]

技术应用

冯·诺依曼结构是现代计算机设计的基础,其核心特点是顺序执行指令,即在任何给定时刻,中央处理单元(CPU)只能执行一个指令。Windows 98/NT操作系统引入了一种先进的任务调度策略,该策略通过将进程细分为多个线程来实现。每个线程都能够轮流占用CPU的运算时间。操作系统通过挂起和唤醒线程,使得多个线程能够交替执行。由于现代CPU的高速度,这种快速的任务切换给用户的感觉就像是多个线程在同时执行,从而模拟了多CPU并行处理的效果。[18]

在多线程技术的一个典型应用中,例如使用资源管理器复制文件时,系统可以同时处理磁盘读写操作和其他任务。在这个过程中,用户可能会看到一个动画效果,如一张纸从一个文件夹“飘”到另一个文件夹,这个动画实际上是由一系列视频剪辑组成的。[18]

典型代码示例

THRCBL QCBLLESRC的源代码[19]

PROCESS NOMONOPRC OPTIONS THREAD(SERIALIZE). IDENTIFICATION DIVISION. PROGRAM-ID. THRCBL. ENVIRONMENT DIVISION. CONFIGURATION SECTION. special-names. system-console is oper1. DATA DIVISION. WORKING-STORAGE SECTION. 01 pthread_attr_t typedef. 05 FILLER PIC 9(8) usage binary occurs 4 times. 05 FILLER USAGE POINTER. 01 pthread_t typedef. 05 FILLER USAGE POINTER. 05 FILLER PIC 9(8) usage binary. 05 FILLER PIC 9(8) usage binary. 05 FILLER PIC 9(8) usage binary. 05 FILLER PIC 9(8) usage binary. 05 FILLER USAGE POINTER. 01 PROC-SUBA-PTR USAGE PROCEDURE-POINTER. 01 PROC-SUBB-PTR USAGE PROCEDURE-POINTER. 01 attr type pthread_attr_t. 01 rc PIC 9(8) usage binary value 0. 01 group1. 05 thread type pthread_t occurs 10 times. 01 joinStatus0 USAGE POINTER. 01 joinStatus1 USAGE POINTER. PROCEDURE DIVISION. TEST1-INIT. SET PROC-SUBA-PTR TO ENTRY PROCEDURE "SUBA". SET PROC-SUBB-PTR TO ENTRY PROCEDURE "SUBB". * Create a thread attributes object call procedure "pthread_attr_init" using attr returning rc. * Define threads to be joinable call procedure "pthread_attr_setdetachstate" using attr by value 0 size 4 returning rc.

* Start creating thread(s) call procedure "pthread_create" using thread(1) attr by value PROC-SUBA-PTR omitted returning rc. call procedure "pthread_create" using thread(2) attr by value PROC-SUBB-PTR omitted returning rc. * Start joining thread(s) call procedure "pthread_join" using by value thread(1) by reference joinStatus0 returning rc. call procedure "pthread_join" using by value thread(2) by reference joinStatus1 returning rc. * Destroy thread attributes object call procedure "pthread_attr_destroy" using attr returning rc.

SUBB QCBLLESRC 的源代码[19]

PROCESS NOMONOPRC OPTIONS THREAD(SERIALIZE). IDENTIFICATION DIVISION. PROGRAM-ID. SUBA. ENVIRONMENT DIVISION. CONFIGURATION SECTION. special-names. system-console is oper1. DATA DIVISION. WORKING-STORAGE SECTION. 01 one-line pic x(11). PROCEDURE DIVISION. TEST1-INIT. move "IN SUBA" TO ONE-LINE. DISPLAY one-line UPON oper1.

SBA QCBLLESRC 的源代码[19]

PROCESS NOMONOPRC OPTIONS THREAD(SERIALIZE). IDENTIFICATION DIVISION. PROGRAM-ID. SUBB. ENVIRONMENT DIVISION. CONFIGURATION SECTION. special-names. system-console is oper1. DATA DIVISION. WORKING-STORAGE SECTION. 01 one-line pic x(11). PROCEDURE DIVISION. TEST1-INIT. move "IN SUBB" TO ONE-LINE. DISPLAY one-line UPON oper1.

参考资料 19

  1. 参考 1
  2. 参考 2
  3. 参考 3
  4. 参考 4
  5. 参考 5
  6. History of Multithreading — people.computing.clemson.edu
  7. 参考 7
  8. 参考 8
  9. 参考 9
  10. 参考 10
  11. 多线程:为处理器提供更高的性能 — www.ionos.de/digitalguide
  12. 参考 12
  13. 参考 13
  14. 参考 14
  15. 参考 15
  16. 参考 16
  17. 参考 17
  18. 参考 18
  19. 多线程示例的样本代码 — IBM
广告位:底部(ad-bottom)—— 请到中台「公共区块」编辑此内容