进程、线程、协程:让你一次搞懂的通俗指南

作为程序员,我们经常听到进程、线程、协程这些概念,但它们到底是什么?有什么区别?今天我们用最通俗的方式来聊聊这三个概念。

先说个生活中的例子

想象你在经营一家餐厅:

  • 进程就像整个餐厅,有自己的厨房、餐具、食材、员工
  • 线程就像餐厅里的厨师,多个厨师共享同一个厨房和食材
  • 协程就像一个厨师在做多道菜,炖汤的时候去炒菜,炒菜等油热的时候去切菜

一、进程(Process):独立的王国

什么是进程?

进程是程序运行的一个实例,是操作系统分配资源的基本单位。每个进程都有自己独立的内存空间,就像每个餐厅都有自己的厨房。

进程的特点

  • 独立性强:每个进程有自己的地盘,互不干扰
  • 资源开销大:开一家新餐厅要租场地、买设备,很贵
  • 通信麻烦:两家餐厅要交流,得打电话或派人跑腿

实际例子

当你打开电脑时:

  • Chrome 浏览器是一个进程
  • 微信是另一个进程
  • VS Code 又是一个进程

它们各自运行,互不影响。Chrome 崩溃了,微信还能正常用。

# Python 创建多进程的例子
from multiprocessing import Process
import os

def worker(name):
    print(f'工作进程 {name},进程ID: {os.getpid()}')
    print(f'父进程ID: {os.getppid()}')

if __name__ == '__main__':
    print(f'主进程ID: {os.getpid()}')
  
    # 创建两个进程
    p1 = Process(target=worker, args=('A',))
    p2 = Process(target=worker, args=('B',))
  
    p1.start()
    p2.start()
  
    p1.join()
    p2.join()
  
    print('所有进程执行完毕')

输出示例:

主进程ID: 12345
工作进程 A,进程ID: 12346
父进程ID: 12345
工作进程 B,进程ID: 12347
父进程ID: 12345
所有进程执行完毕

二、线程(Thread):共享资源的工人

什么是线程?

线程是进程内的执行单元,是 CPU 调度的基本单位。一个进程可以有多个线程,它们共享进程的资源。

线程的特点

  • 共享内存:同一餐厅的厨师共享厨房和食材
  • 切换快速:厨师之间换班比开新餐厅快多了
  • 互相影响:一个厨师打翻油锅,整个厨房都遭殃

实际例子

Chrome 浏览器这个进程里:

  • 一个线程负责渲染页面
  • 一个线程负责处理用户输入
  • 一个线程负责网络请求
  • 多个线程负责执行 JavaScript
# Python 创建多线程的例子
import threading
import time

def download_file(file_name):
    print(f'开始下载 {file_name}')
    time.sleep(2)  # 模拟下载耗时
    print(f'{file_name} 下载完成')

# 创建多个线程同时下载
threads = []
files = ['电影.mp4', '音乐.mp3', '文档.pdf', '图片.jpg']

for file in files:
    t = threading.Thread(target=download_file, args=(file,))
    threads.append(t)
    t.start()

# 等待所有线程完成
for t in threads:
    t.join()

print('所有文件下载完成!')

输出:

开始下载 电影.mp4
开始下载 音乐.mp3
开始下载 文档.pdf
开始下载 图片.jpg
电影.mp4 下载完成
音乐.mp3 下载完成
文档.pdf 下载完成
图片.jpg 下载完成
所有文件下载完成!

注意:四个文件几乎同时开始,同时结束,总耗时约 2 秒,而不是 8 秒。

三、协程(Coroutine):聪明的多任务

什么是协程?

协程是一种用户态的轻量级线程,由程序自己控制调度。就像一个厨师在等水开的时候去切菜,充分利用等待时间。

协程的特点

  • 极其轻量:创建成千上万个协程毫无压力
  • 主动让出:程序自己决定什么时候切换
  • 适合 I/O:特别适合网络请求、文件读写等需要等待的任务

实际例子

爬虫程序:

  • 发送请求后要等服务器响应
  • 等待期间可以去处理其他请求
  • 不需要创建大量线程
# Python asyncio 协程示例
import asyncio
import time

async def cook_soup():
    print('开始煮汤')
    await asyncio.sleep(3)  # 模拟煮汤需要 3 秒
    print('汤煮好了')
    return '一碗汤'

async def cook_rice():
    print('开始煮饭')
    await asyncio.sleep(2)  # 模拟煮饭需要 2 秒
    print('饭煮好了')
    return '一碗饭'

async def stir_fry():
    print('开始炒菜')
    await asyncio.sleep(1)  # 模拟炒菜需要 1 秒
    print('菜炒好了')
    return '一盘菜'

async def prepare_dinner():
    print('开始准备晚餐...\n')
    start = time.time()
  
    # 同时进行三个任务
    results = await asyncio.gather(
        cook_soup(),
        cook_rice(),
        stir_fry()
    )
  
    end = time.time()
    print(f'\n晚餐准备完毕:{results}')
    print(f'总耗时:{end - start:.1f} 秒')

# 运行协程
asyncio.run(prepare_dinner())

输出:

开始准备晚餐...

开始煮汤
开始煮饭
开始炒菜
菜炒好了
饭煮好了
汤煮好了

晚餐准备完毕:['一碗汤', '一碗饭', '一盘菜']
总耗时:3.0 秒

如果按顺序做,需要 3 + 2 + 1 = 6 秒。用协程并发执行,只需 3 秒!

四、三者对比

形象比喻

概念 比喻 特点
进程 不同的餐厅 独立运营,互不干扰,开销大
线程 餐厅里的厨师 共享厨房,协作工作,开销中等
协程 一个厨师做多道菜 充分利用等待时间,开销极小

技术对比

特性 进程 线程 协程
调度者 操作系统内核 操作系统内核 用户程序
切换开销 大(毫秒级) 中(微秒级) 小(纳秒级)
创建数量 几十到几百 几百到几千 几千到几万
内存占用 独立(几 MB) 共享(几百 KB) 共享(几 KB)
并行执行 ✅ 真并行 ✅ 真并行 ❌ 并发非并行
适用场景 CPU 密集型 CPU 密集型 I/O 密集型

五、什么时候用什么?

用进程的场景

  • 隔离性要求高:比如浏览器的每个标签页,一个崩溃不影响其他
  • 多核 CPU 利用:科学计算、视频转码等 CPU 密集任务
  • 不同程序语言:不同模块用不同语言编写
# 计算密集型任务用多进程
from multiprocessing import Pool

def calculate_heavy(n):
    return sum(i * i for i in range(n))

if __name__ == '__main__':
    with Pool(4) as p:  # 4 个进程
        results = p.map(calculate_heavy, [10000000] * 4)
    print(f'计算完成:{results}')

用线程的场景

  • 共享数据多:需要频繁访问共享变量
  • 响应式 UI:界面程序,主线程处理 UI,其他线程处理任务
  • 中等并发:不需要成千上万的并发连接
// Java 下载管理器
public class DownloadManager {
    public void downloadFiles(List<String> urls) {
        ExecutorService executor = Executors.newFixedThreadPool(10);
      
        for (String url : urls) {
            executor.submit(() -> {
                downloadFile(url);
            });
        }
      
        executor.shutdown();
    }
}

用协程的场景

  • 高并发 I/O:Web 服务器、爬虫、聊天服务器
  • 网络请求密集:API 调用、数据库查询
  • 资源受限:不能创建太多线程的场景
// Node.js Web 服务器(天然支持协程)
const express = require('express');
const app = express();

app.get('/user/:id', async (req, res) => {
    // 并发查询数据库和缓存
    const [user, cache] = await Promise.all([
        db.query('SELECT * FROM users WHERE id = ?', req.params.id),
        redis.get(`user:${req.params.id}`)
    ]);
  
    res.json(user || cache);
});

app.listen(3000); // 可以同时处理成千上万个请求

六、实战案例:爬虫对比

同样是爬取 100 个网页,看看三种方式的差别:

1. 单线程同步(最慢)

import requests
import time

def fetch_sync(urls):
    results = []
    for url in urls:
        response = requests.get(url)
        results.append(response.text)
    return results

# 耗时约:100 秒(假设每个请求 1 秒)

2. 多线程(快)

from concurrent.futures import ThreadPoolExecutor

def fetch_threaded(urls):
    with ThreadPoolExecutor(max_workers=10) as executor:
        results = list(executor.map(requests.get, urls))
    return results

# 耗时约:10 秒(10 个线程并发)

3. 协程(最快)

import asyncio
import aiohttp

async def fetch_async(urls):
    async with aiohttp.ClientSession() as session:
        tasks = [session.get(url) for url in urls]
        responses = await asyncio.gather(*tasks)
        return [await r.text() for r in responses]

# 耗时约:1-2 秒(可以创建 100 个协程同时请求)

七、常见误区

误区 1:线程越多越快?

错! 线程太多会导致频繁的上下文切换,反而降低性能。通常线程数应该接近 CPU 核心数(CPU 密集型)或稍多一些(I/O 密集型)。

误区 2:协程可以利用多核?

错! 单个线程内的协程无法利用多核。如果要利用多核,需要多进程 + 协程的组合。

误区 3:进程一定比线程安全?

⚠️ 不完全对! 进程间通信如果使用共享内存,同样有并发问题。

总结

  • 进程:独立的房子,隔离性好,开销大,适合需要隔离或多核计算的场景
  • 线程:共享的房间,通信方便,开销中等,适合需要共享数据的并发任务
  • 协程:聪明的多任务,轻量高效,开销极小,适合高并发 I/O 密集型任务

记住这个选择公式:

  • CPU 密集 + 多核 → 多进程
  • 共享数据多 → 多线程
  • 高并发 I/O → 协程

希望这篇文章能帮你彻底理解这三个概念!如果还有疑问,欢迎留言讨论。


最后提醒:实际项目中,这三者常常组合使用。比如 Web 服务器可能是:多进程(利用多核)+ 每个进程内用协程(处理高并发请求)。

后记

2026年8月10日于上海,在claude opus 4.8辅助下完成。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐