首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >三大并发技术—进程、线程和协程

三大并发技术—进程、线程和协程

作者头像
霞姐聊IT
发布2025-11-12 20:00:29
发布2025-11-12 20:00:29
3480
举报

对于程序员来说,不管是为了提升用户体验,还是为了压榨硬件性能,在工作中早晚都会使用到并发编程技术。进程、线程、协程是并发编程涉及到的三种技术:进程是走进并发的基础,线程是榨干多核的利器,则协程技术则进一步提升了IO密集型应用的性能。

一、进程(process)

1.进程的起源

进程(process)是操作系统中最古老、最重要的抽象之一。

1961年,计算机领域的泰山北斗——费尔南多·何塞·科尔巴托在CTSS(Compatible Time-Sharing System)中做出了进程的原型,但没有使用进程这个术语。进程这个术语正式提出于他1964 年领导的MULTICS 项目。

想了解最早的process是如何设计的,可以参考这篇文章“Structure of the Multics Supervisor(www.multicians.org/fjcc3.html)”。

2.进程是什么?

直观来讲,进程就是一个正在运行的程序的实例。

程序和进程有什么区别呢?

程序员打开IDE,用c语言写了段代码,再把它编译成了一个可执行文件aaa。这时候的源文件、可执行文件,是静态存在的程序。

而当程序员用./aaa,让程序在系统里面运行起来时,这期间运行的程序叫进程。

如果程序运行了两遍,则算作两个进程。

在linux系统上,可以用ps aux命令查看当前运行的所有进程。

3.进程解决什么问题?

进程首先解决的是CPU利用率的问题。

在没有进程之前,早期计算机中的程序是串行执行的。但是当一个程序在执行读写磁盘等I/O操作时,CPU只能空闲等待,直到I/O操作完成。

而I/O速度的设备比CPU慢几个数量级,这样CPU大部分时间只能闲着,利用率极低。

因此计算机科学家发明了“进程”。有了进程之后,操作系统可以同时将多个程序加载到内存中,当一个进程需要等待I/O时,操作系统就会挂起它,并将CPU分配给另一个已经准备好的进程。

这样CPU就充分被利用起来了。

不过,有了进程之后,由于进程被设计成拥有独立的、受保护的虚拟地址空间,也很好的提升了系统的安全性和稳定性。

4.进程控制块(PCB)

操作系统为每个进程维护一个 PCB,包含以下关键信息:

(1)进程管理

寄存器、程序计数器、程序状态字、堆栈指针、进程状态、优先级、调度参数、进程ID、父进程、进程组、信号、进程开始时间、使用的CPU时间、子进程的CPU时间、下次定时器时间

(2)存储管理

正文段指针、数据段指针、堆栈段指针

(3)文件管理

根目录、工作目录、文件描述符、用户ID、组ID

每个进程都有自己独立的寄存器、内存、I/O资源、控制流这些上下文信息,这些信息就保存在PCB中。

一个进程虽然可能在运行过程中被打断多次,但每次打断时,进程的上下文都会被保存,以便在切换后通过恢复该上下文,返回到和打断前完全相同的状态。

5.Linux系统中进程的创建与管理函数

(1)Fork()用来创建新进程

Fork()由clone()实现。调用fork后,内核为调用进程创建了一个子进程。子进程是父进程的副本。

(2)子进程执行exec()

内核将一个新的程序的代码和数据加载到子进程的地址空间,替换原有的内容。

注意,exec是一个函数族,指的是execl,execlp,execle,execv,execve和execvp这些函数。

(3)父进程调用wait/waitpid

等待子进程结束,并回收子进程的资源,包括僵尸进程。

(4)子进程调用Exit

通知内核,当前进程已完成,其数据结构(栈、堆、代码)需被垃圾回收。

6.进程间如何通信

进程和进程之间交换信息/通信被称为IPC(InterProcess Communication)。Linux可使用:管道、Sockets、共享内存、信号量、消息队列等方式实现进程间通信。

(1)管道: 用于两个相关进程之间 IPC 的单向通信通道。一个进程写入管道,另一个进程从管道读取。管道类型包括匿名管道和命名管道 (FIFO)

(2)Sockets:套接字用于在不同主机上运行的进程之间的网络通信。Sockets提供了一个标准的通信接口,可以跨不同的平台和编程语言使用。

(3)共享内存:多个进程可以访问公共内存空间。进程可以向该存储空间读取和写入数据,从而实现它们之间的快速通信。

(4)信号量:信号量用于控制对共享资源的访问。可防止多个进程同时访问同一资源,从而导致数据损坏。

(5)消息队列:使用单个队列或多个消息队列在进程之间传递消息。

二、线程(thread)

1.线程的历史

线程出现的比进程晚一些。

线程的概念萌芽于上世纪60年代末。例如,1968 年IBM 为大型机开发的系统TSS/360就尝试了“进程内多执行流”的技术,允许一个 “主进程” 内创建多个“子任务(Subtask)”,子任务可共享主进程的内存和资源,但拥有独立的程序计数器(PC)和栈。

线程在80年代逐渐演进为OS的核心进制,尤其是1987 年SunOS 4.0的内核级线程机制,让线程具备了高效并发+多CPU利用的能力。

线程在90年代中期后解决了标准化的问题。1995年,IEEE制定了POSIX 1003.1c 标准,定义了跨 UNIX 系统的线程接口pthreads,这样,开发者开发的多线程软件就可以跨平台了。

2.线程是什么

线程是运行在进程上下文中的逻辑流,是操作系统调度的最小执行单元。一个进程中必须至少有一个线程,也可以在一个进程中创建多个线程。

每个线程有自己的id、程序计数器、寄存器、堆栈。同一个进程中的多个线程之间共享地址空间、全局变量、打开文件、子进程、定时器等等。

3.线程解决什么问题?

为什么有了进程,还需要线程这个抽象呢?线程是解决什么问题的呢?

进程有自己的地址空间,所以进程之间的隔离性比较好,但是进程的创建、进程间的通信等开销都比较厚重。

打个比方来说,每个进程就好比一个部门,每个部门有自己独立的办公室、预算、资源,部门间不能随意动用对方的资源,部门间的沟通要做正式的流程,部门墙比较厚重。

而线程呢,就好比部门里面的员工,所有员工共享办公室、预算、资源。互相之间的沟通,吼一嗓子就可以了,效率比较高。

线程的创建/销毁开销较小、上下文切换的开销也小、通信机制简单、数据共享性好、也有更好的并发性能和响应能力。

4.Pthreads

pthreads即POSIX Threads,是IEEE POSIX 1003.1c 标准定义的跨平台线程 API。包括线程管理、互斥锁、条件变量、同步管理这四类共100多个函数。

比如,创建线程可用pthread_create(),终止线程可用pthread_exit(),阻塞当前的线程,直到另外一个线程运行结束可用pthread_join(),让出处理器的调用,让其他线程获得执行的机会可用pthread_yield()。

5.并发问题

程序员在多线程编程时要注意并发问题,比如:数据竞争、死锁、伪共享等等。

三、协程

1.协程的历史

协程的历史很有趣,虽然近些年来协程才流行起来,但如果追溯历史的话,协程这个概念在1958年就被Melvin Conway提出了,Conway将协程描述为 “像主程序一样工作的子例程”,并在 COBOL 编译器中实现了这一结构,以简化词法分析器与语法分析器之间的协作

1967年,面向对象语言的鼻祖Simula 67把协程写进了语言标准。

但80年代到2000年,随着线程的兴起,工业界的焦点集中在线程上,协程比较沉寂。

而2000年之后,协程又进入了复兴期,尤其是将协程(Goroutine)作为其并发模型的核心Go语言的横空出世,带来了协程的出圈效应。

2.协程是什么

线程是存在于进程中的调度单位,而协程又是存在于线程中的更小的调度单位。

协程可以直观的理解成一种能够被挂起,稍后再在挂起的地方恢复的函数。挂起和恢复是开发者的程序逻辑自己控制的。

协程实现可分为有栈协程和无栈协程:

有栈协程:为每个协程分配独立的栈空间。当协程挂起时,需要保存当前的执行上下文。切换时,恢复目标协程的完整上下文。

无栈协程:不分配独立的栈空间,而是通过状态机来实现。编译器将协程函数转换为一个状态机,用局部变量保存执行状态。

无栈协程由编译器生成状态机,线程切换本质上相当于普通的函数调用,性能上比有栈协程占优;无栈协程无需为每个协程单独分配独立的栈空间,仅存储活跃变量,内存开销较有栈协程占优,因此近年来逐渐成为主流。

3.协程解决什么问题

互联网时代带来了前所未有的并发连接数挑战,IO密集型应用随之爆发。而如果只使用原有的多线程编程,会带来很大的内存开销和调度开销,并且会增大程序员编程的复杂性。

协程(尤其是无栈协程)的资源占用是极轻量级的、纯用户态的调用开销也较线程降低一个数量级。

使用协程编程,编程清晰,可用同步的线性代码风格完成异步的逻辑,也会降低编程的复杂度。

四、结语

进程奠定了并发的基础,通过独立的地址空间实现了程序的隔离与保护,解决了CPU利用率的问题,是系统稳定性的基石。

线程则在进程内部实现了更细粒度的并发,极大地降低了创建、切换和通信的开销,充分发挥了多核CPU的计算能力,是提升应用性能的利器。

协程将并发的粒度进一步细化到函数级别,以其轻量级和可协作的特性,完美契合了现代高并发、I/O密集型的应用场景,并用同步的编码风格化解了异步的复杂性。

这三者并非简单替代,而是互为补充,体现了计算机领域工作者追求卓越的精神。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-10-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 霞姐聊IT 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档