线程(英語:thread)在计算机科学中,是将进程划分为两个或多个线程(实例)或子进程,由单处理器(单线程)或多处理器(多线程)或多核处理系统并发执行。

「thread」的各地常用譯名
中國大陸线程
港澳線程
臺灣執行緒

概述

编辑

在现代操作系统中,进程是资源分配的基本单位,而线程是CPU调度和执行的基本单位[1]。同一进程内的多个线程共享该进程的虚拟地址空间、全局变量、打开的文件描述符等资源,但每个线程拥有独立的程序计数器寄存器组和

与创建新进程相比,创建线程的开销显著更小,因为线程不需要复制整个地址空间。同一进程内的线程之间切换也比进程切换更快,因为不需要切换地址空间和刷新TLB。这些特性使得线程成为实现并发计算的主要手段。

线程与进程的比较

编辑
比较项 进程 线程
地址空间 独立 共享所属进程的地址空间
资源开销 创建和销毁开销大 创建和销毁开销小
上下文切换 需要切换地址空间,开销大 仅切换寄存器和栈,开销小
通信方式 需要进程间通信(IPC) 可直接读写共享内存
隔离性 一个进程崩溃不影响其他进程 一个线程崩溃可能导致整个进程终止
独立资源 地址空间、文件描述符、信号处理 程序计数器、寄存器、栈

多线程的优势

编辑

引入多线程的主要动机包括[1]

  • 响应性:在GUI应用中,将耗时操作放在后台线程执行,可以避免主线程(UI线程)被阻塞,保持界面对用户操作的响应。
  • 资源共享:同一进程内的线程天然共享内存和文件等资源,无需借助IPC机制,编程模型更简单。
  • 经济性:线程的创建、销毁和上下文切换的开销远小于进程。
  • 可扩展性:多线程程序可以在多核处理器上实现真正的并行执行,充分利用硬件资源。

线程的实现模型

编辑

根据线程的管理者是用户空间还是内核,线程的实现可分为以下几种模型[1]

用户级线程

编辑

用户级线程完全在用户空间由线程库管理,内核对其存在毫不知情。优点是线程切换不需要陷入内核态,速度快;缺点是当一个用户级线程执行阻塞性系统调用时,整个进程(包括其中所有线程)都会被阻塞。

内核级线程

编辑

内核级线程由操作系统内核直接管理和调度。每个线程都是内核的调度单位,一个线程阻塞不影响同一进程中的其他线程。缺点是线程的创建和切换需要系统调用,开销比用户级线程大。现代主流操作系统(LinuxWindowsmacOS)均原生支持内核级线程。

混合模型

编辑

某些系统采用多对多模型(M:N模型),将   个用户级线程映射到   个内核级线程上( )。这种模型兼顾了用户级线程的轻量性和内核级线程的并发能力。Go语言Goroutine采用的就是这种模型的变体。

线程安全与同步

编辑

由于同一进程内的线程共享地址空间,多个线程同时访问共享数据时可能产生竞争条件,导致程序行为不可预测。为此,操作系统和编程语言提供了多种同步机制[1]

  • 互斥锁:保证在任一时刻只有一个线程能进入临界区
  • 信号量:广义的互斥锁,允许最多   个线程同时访问资源。
  • 条件变量:允许线程在特定条件不满足时挂起等待,条件满足时被唤醒。
  • 读写锁:允许多个读线程并发访问,但写线程必须独占。
  • 自旋锁:线程在等待锁时不挂起而是循环检测,适用于锁持有时间极短的场景。

不当使用同步机制可能导致死锁(两个或多个线程互相等待对方释放锁)、活锁(线程不断重试但无法推进)或优先级反转等问题。

如果一个函数或代码段在多线程环境下可以被安全地并发调用,则称其为线程安全的。实现线程安全的常见策略包括使用同步原语保护共享数据、使用线程局部存储以及设计无状态或不可变的数据结构。

狀態

编辑

執行緒有四種基本狀態,分別為:

  • 產生(spawn
  • 阻塞(block
  • 非阻塞(unblock
  • 結束(finish

线程包含要素

编辑
  • 线程内核对象(thread kernel object)
  • 线程环境块(thread environment block, TEB)
  • 用户模式栈(user-mode stack)(unblock
  • 内核模式栈(kernal-mode stack)(thread environment block, TEB)
  • DLL线程连接(attach)和线程分离(detach)通知(kernal-mode stack)

不同平台的线程

编辑

UNIX International线程

编辑

UNIX International线程简介

编辑

SUN Solaris操作系统使用的线程叫做UNIX International线程,支持内核线程、轻权进程和用户线程。一个进程可有大量用户线程;大量用户线程复用少量的轻权进程,轻权进程与内核线程一一对应。用户级线程在调用核心服务时(如文件读写),需要“捆绑(bound)”在一个LWP上。永久捆绑(一个LWP固定被一个用户级线程占用,该LWP移到LWP池之外)和临时捆绑(从LWP池中临时分配一个未被占用的LWP)。在调用系统服务时,如果所有LWP已被其他用户级线程所占用(捆绑),则该线程阻塞直到有可用的LWP。如果LWP执行系统线程时阻塞(如read()调用),则当前捆绑在LWP上的用户级线程也阻塞。

UNIX International线程的有关API

编辑

UNIX International线程的头文件是<thread.h>[2]

创建用户级线程
编辑
int thr_create(void * stack_base, size_t stack_size, void *(*start_routinevoid *), void * arg, long flags, thread_t * new_thr);

其中flags包括:THR_BOUND(永久捆绑), THR_NEW_LWP(创建新LWP放入LWP池),若两者同时指定则创建两个新LWP,一个永久捆绑而另一个放入LWP池。

等待用户级线程
编辑
int thr_join(thread_t wait_for, thread_t *dead, void **status);
挂起用户级线程
编辑
int thr_suspend(thread_t thr);
继续用户级线程
编辑
int thr_continue(thread_t thr);
退出用户级线程
编辑
void thr_exit(void *status);
返回当前用户级线程的线程标识符
编辑
thread_t thr_self( void );

POSIX线程

编辑

POSIX线程简介

编辑

POSIX线程(POSIX threads),简称Pthreads,是线程的POSIX标准。该标准定义了创建和操纵线程的一整套API。在类Unix操作系统(UnixLinuxMac OS X等)中,都使用Pthreads作为操作系统的线程[3][4][5]Windows操作系统也有其移植版pthreads-win32[6]

POSIX线程的有关API

编辑

Pthreads线程的头文件是<pthread.h>[7][8]

创建用户线程
编辑
int pthread_create(pthread_t * thread, const pthread_attr_t * attr, void *(*start_routine)(void *), void *arg);
等待用户线程
编辑
int pthread_join(pthread_t thread, void ** retval);
退出用户线程
编辑
void pthread_exit(void *retval);
返回当前用户线程的线程标识符
编辑
pthread_t pthread_self(void);
用户线程的取消
编辑
int pthread_cancel(pthread_t thread);

Win32线程

编辑

Win32线程简介

编辑

Win32线程是Windows API的一部分,上下文包括:寄存器、核心栈、线程环境块和用户栈。

Win32线程的有关API

编辑

Win32线程的头文件是<Windows.h>,仅适用于Windows操作系统。[9]

创建用户线程
编辑
HANDLE WINAPI CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId);
结束本线程
编辑
VOID WINAPI ExitThread(DWORD dwExitCode);
挂起指定的线程
编辑
DWORD WINAPI SuspendThread( HANDLE hThread );
恢复指定线程运行
编辑
DWORD WINAPI ResumeThread(HANDLE hThread);
等待线程运行完毕
编辑
DWORD WINAPI WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);
返回当前线程的线程标识符
编辑
DWORD WINAPI GetCurrentThreadId(void);
返回当前线程的线程句柄
编辑
HANDLE WINAPI GetCurrentThread(void);

跨平台的线程

编辑

C++11线程

编辑

C++11线程简介

编辑

2011年8月12日,国际标准化组织(ISO)发布了第三个C++标准,即ISO/IEC 14882:2011,简称ISO C++ 11标准。该标准第一次把线程的概念引入C++标准库。Windows平台运行的VS2012Linux平台运行的g++4.7,都完美支持C++11线程。

C++11线程的有关函数

编辑

C++ 11线程的头文件是<thread>[10]

创建线程
编辑
std::thread::thread(Function&& f, Args&&... args);
等待线程结束
编辑
std::thread::join();
脱离线程控制
编辑
std::thread::detach();
交换线程
编辑
std::thread::swap(thread& other);

C11线程

编辑

C11线程简介

编辑

2011年12月8日,国际标准化组织(ISO)发布了第三个C语言标准,即ISO 9899:2011,简称ISO C 11标准。该标准第一次把线程的概念引入C语言标准库。

C11线程仅仅是个“建议标准”,也就是说100%遵守C11标准的C编译器是可以不支持C11线程的。根据C11标准的规定,只要编译器预定义了 __STDC_NO_THREADS__(C11),就可以没有<threads.h>头文件,自然也就也没有下列函数。

C11线程的有关函数

编辑

C11线程的头文件是<threads.h>[11]

创建线程
编辑
int thrd_create(thrd_t *thr, thrd_start_t func, void *arg);
结束本线程
编辑
_Noreturn void thrd_exit( int res );
等待线程运行完毕
编辑
int thrd_join(thrd_t thr, int *res);
返回当前线程的线程标识符
编辑
thrd_t thrd_current();

Java线程

编辑
  1. 最简单的情况是,Thread/Runnablerun()方法运行完毕,自行终止。
  2. 对于更复杂的情况,比如有循环,则可以增加终止标记变量和任务终止的检查点。
  3. 最常见的情况,也是为了解决阻塞不能执行检查点的问题,用中断来结束线程,但中断只是请求,并不能完全保证线程被终止,需要执行线程协同处理。
  4. IO阻塞和等锁情况下需要通过特殊方式进行处理。
  5. 使用Future类的cancel()方法调用。
  6. 调用线程池执行器的shutdown()shutdownNow()方法。
  7. 守护线程会在非守护线程都结束时自动终止。
  8. Thread有stop()方法,但已不推荐使用。

参见

编辑

参考资料

编辑
  1. ^ 1.0 1.1 1.2 1.3 Abraham Silberschatz; Peter B. Galvin; Greg Gagne. Chapter 4: Threads & Concurrency. Operating System Concepts 10th. Wiley. 2018. ISBN 978-1-119-32091-3. 
  2. ^ Novell Doc: NDK: Libraries for C (LibC), Volume 2 - UI Thread Functions页面存档备份,存于互联网档案馆), NOVELL Worldwide
  3. ^ pthreads (7) 互联网档案馆存檔,存档日期2013-10-08., UNIX man pages
  4. ^ pthreads (7)页面存档备份,存于互联网档案馆), Linux manual page
  5. ^ pthread (3) Mac OS X Developer Tools Manual Page页面存档备份,存于互联网档案馆), Apple Developer
  6. ^ POSIX Threads (pthreads) for Win32页面存档备份,存于互联网档案馆), sourceware.org: Free software! Get your fresh hot free software!
  7. ^ PTHREAD_CREATE页面存档备份,存于互联网档案馆), Linux Man Pages
  8. ^ POSIX Threads Programming页面存档备份,存于互联网档案馆), High Performance Computing: High Performance Computing
  9. ^ Multiple Threads (Windows)页面存档备份,存于互联网档案馆), MSDN-the microsoft developer network
  10. ^ std::thread页面存档备份,存于互联网档案馆), cppreference.com
  11. ^ Thread support library页面存档备份,存于互联网档案馆), cppreference.com

📚 Artikel Terkait di Wikipedia

Windows Embedded Compact

WinCE的許多APIs功能都受限,如CreateThread函数在许多参数在Windows CE下都不支持,第1、2、5的參數值必須设为NULL或0。 HThread = CreateThread(NULL, 0, Thread, nParameter, 0, &dwThreadID); 微軟操作系统列表 Microsoft

并行编程模型

state per thread, including a program counter and call stack, and can yield execution at a per-thread granularity(英语:Granularity (parallel computing)), either

Python

Python functions as pipeline jobs. [2022-09-19]. (原始内容存档于2022-12-07).  Thread-pool Controls. [2022-09-19]. (原始内容存档于2022-11-02).  TensorFlow API Documentation

并行计算

并行计算(英語:Parallel computing)是一种计算模式,指将多个计算任务或进程同时进行处理。 庞大复杂的问题通常可以被拆分为多个较小的部分,随后在同一时间内被并行求解。并行计算有几种不同的形式:位级并行、指令级并行、数据并行以及任务并行。并行计算长期以来一直被应用于高性能计算,但随着阻碍频率缩放(英语:frequency

監視器 (程序同步化)

"the signaled thread") add this thread to s restart t (so t will occupy the monitor next) block this thread schedule : if there is a thread on s select

伪共享

theThread = []( atomic_type &atomicValue ) { for( size_t r = 100'000'000; r--; ) ++atomicValue; }; jthread threadA( theThread, sharedOrNot.a ), threadB(

C++11

C++11標準函式庫會提供類別thread(std::thread)。若要執行一個執行緒,可以建立一個類別thread的實體,其初始參數為一個函式物件,以及該函式物件所需要的參數。透過成員函式std::thread::join()對執行緒會合的支援,一個執行緒可以暫停直到

PL/I

PL/I 编程范型 过程式, 指令式, 结构化 設計者 IBM与SHARE(英语:SHARE (computing))语言发展委员会 實作者 IBM 发行时间 1964年,​62年前​(1964) 網站 www.ibm.com/products/pli-compiler-zos 衍生副語言 PL/M(英语:PL/M)