多线程编程:线程与进程基础

一、线程与进程的核心区别

本质差异

  • 进程是操作系统进行资源分配的基本单位拥有完整的资源集合,包括代码段、数据段、堆、栈、打开的文件句柄等;

  • 线程是CPU调度和执行的基本单位,不拥有独立的资源,依托所属进程的资源运行,仅独占自身的栈空间、寄存器和程序计数器,多个线程共享同一进程的所有资源。

资源共享

  • **进程间的虚拟地址空间相互独立,默认不共享任何资源,**若需实现进程间资源共享,必须借助IPC(进程间通信)机制,常见的有管道、共享内存、消息队列、信号量等;

  • 同一进程内的所有线程,因共享进程的虚拟地址空间,可直接访问进程内的全局变量、堆内存、静态变量、打开的文件等资源,无需额外通信机制。

切换开销

  • 进程切换时,需要切换整个进程的资源上下文 ,包括页表、寄存器、栈、文件句柄等,切换成本高、消耗系统资源多,效率较低;

  • 线程切换仅需切换自身的执行上下文(寄存器、程序计数器、栈指针),无需切换进程资源和页表(线程共享进程页表),切换开销远小于进程,并发效率更高。

稳定性

  • 进程之间相互独立,每个进程拥有独立的地址空间和资源,一个进程的崩溃(如内存错误、异常退出)不会影响其他进程的正常运行;

  • 线程共享进程的资源和地址空间,若一个线程触发内存错误(如野指针、栈溢出),会破坏进程的资源结构和地址空间,导致整个进程及所属所有线程崩溃,稳定性远低于进程。

跨平台差异

  • Windows系统:线程是真正的调度实体,进程仅作为资源分配的单位,不参与CPU调度;主线程从主函数(main函数)开始执行,所有线程均可被操作系统直接调度,存在就绪态、执行态、I/O阻塞态三种基本状态的切换,线程的创建和调度由操作系统内核直接管理。

  • Linux系统:线程本质是轻量级进程(LWP),进程本身也是CPU调度的基本单位;主线程所在的执行体即为进程,其他线程均为该进程的轻量级分支,共享进程的资源和地址空间;线程的调度本质上是轻量级进程的调度,进程切换的资源消耗远大于线程切换,Linux通过clone系统调用创建线程,共享进程的页表、文件描述符等资源。

二、线程数量限制

  • 32位系统限制原因:32位系统的虚拟地址空间总大小为4GB,在Linux系统中,默认1GB分配给内核态,剩余3GB分配给用户态;每个线程都有独立的栈空间,栈空间的大小由系统默认配置(通常为8MB),所有线程的栈空间总和不能超过用户态的3GB虚拟地址空间,因此线程数量由"3GB / 单个线程栈大小"决定。理论上,32位系统的线程数量约为3GB/8MB≈384个,实际数量会更少,因为用户态地址空间还需分配给堆内存、共享库等其他资源,无法全部用于线程栈。

  • 64位系统限制原因:64位系统的虚拟地址空间理论上可达2^64,实际可用的用户态虚拟地址空间约为256TB,远大于实际的物理内存大小,因此线程数量不再受虚拟地址空间的限制。64位系统中,线程数量的核心瓶颈是物理内存大小和CPU调度开销:每个线程的栈空间和线程控制块(TCB)需要占用物理内存,物理内存不足会导致线程创建失败;同时,线程数量过多会导致CPU上下文切换频繁,消耗大量CPU资源,反而降低程序的并发效率,因此线程数量需根据CPU核心数和任务类型合理设置。

三、线程与进程的地址空间特性

  1. 进程间的虚拟地址空间相互独立,每个进程都有自己的页表,将虚拟地址映射到物理地址,进程之间无法直接访问对方的虚拟地址空间,若需通信,必须通过IPC机制实现资源共享或消息传递。

  2. 线程属于某个特定的进程,所有线程共享该进程的虚拟地址空间、页表和物理内存资源,因此线程可直接访问进程内的全局变量、堆内存、静态变量等共享资源;但线程也有自己独立的栈空间,栈空间用于存储线程的局部变量、函数栈帧等,线程之间的栈空间相互独立,互不干扰,避免了线程间的栈数据冲突。

补充:

1.函数被调用时,会在当前线程的栈上开辟独立的栈帧,每调用一次函数就会开辟一块新的栈帧,栈空间的生长方向为"向上"(即从高地址向低地址生长);

2.栈帧的生命周期与函数调用一致,函数执行完毕后,栈帧自动释放,局部变量也随之销毁。

3.线程的栈空间和堆空间有明显区别:

  • 栈空间由系统自动分配和释放,每个线程独有,存储局部变量、函数栈帧;
  • 堆空间由程序员手动分配(如new、malloc)和释放(如delete、free),所有线程共用,存储动态分配的内存,堆空间的生命周期由程序员控制,若未及时释放,会导致内存泄漏。

问题总结:

进程和线程的本质区别是什么?

核心区别是定位不同:

进程是资源分配的基本单位,拥有完整的资源集合(代码段、数据段、堆、栈等);

线程是CPU调度和执行的基本单位,不拥有独立资源,依托所属进程的资源运行,仅独占栈、寄存器、程序计数器。

简单总结为"进程管资源,线程管执行"。

Linux下的线程和Windows下的线程有什么区别?

核心差异在于调度实体和实现方式:

① Windows中,线程是真正的调度实体,进程仅作为资源单位,线程由操作系统内核直接调度;② Linux中,线程本质是轻量级进程(LWP),进程也是调度实体,线程的调度本质是轻量级进程的调度,线程通过clone系统调用创建,共享进程的页表和资源。

32位和64位系统中,线程数量的限制原因分别是什么?

① 32位系统:虚拟地址空间共4GB,用户态仅3GB,每个线程有独立栈空间,线程数量受"3GB / 单线程栈大小"限制,受虚拟地址空间约束;

② 64位系统:虚拟地址空间极大(实际用户态256TB),不受虚拟地址限制,仅受物理内存大小(线程栈和TCB占用物理内存)和CPU调度开销约束。

为什么线程切换比进程切换效率高?

因为切换的上下文不同:进程切换需要切换整个资源上下文(页表、寄存器、栈、文件句柄等),开销大;线程切换仅需切换自身的执行上下文(寄存器、程序计数器、栈指针),无需切换进程资源和页表(线程共享进程页表),因此切换开销远小于进程。

线程的栈空间和堆空间有什么区别?

① 栈空间:每个线程独有,由系统自动分配和释放,存储局部变量、函数栈帧,生长方向从高地址到低地址,生命周期与函数调用一致;

② 堆空间:所有线程共用,由程序员手动分配和释放,存储动态内存,生命周期由程序员控制,未及时释放会导致内存泄漏。

相关推荐
炘爚4 小时前
多线程编程:生产者消费者模型
多线程·系统编程·生产者消费者模型
lee_curry3 天前
JUC第一章 java中基础概念和CompletableFuture
java·多线程·并发·juc
AIminminHu4 天前
OpenGL渲染与几何内核那点事-项目实践理论补充(三-1-(3):番外篇-当你的CAD打开“怪兽级”STL时:从内存爆炸到零拷贝的极致优化)
开发语言·c++·线程·多线程
rqtz6 天前
【C++】ROS2捕获Ctrl+C信号+原子操作与线程生命周期控制
c++·多线程·原子
爱码驱动10 天前
Java多线程详解(5)
java·开发语言·多线程
派大星酷11 天前
Java 多线程创建方式
java·开发语言·多线程
书到用时方恨少!15 天前
Python threading 使用指南:并发编程的轻骑兵
python·多线程·thread·多任务
Zzzzmo_16 天前
【JavaEE】多线程01
java·jvm·java-ee·多线程
十年编程老舅18 天前
Linux 多线程高并发编程:读写锁的核心原理与底层实现
linux·c++·linux内核·高并发·线程池·多线程·多进程