在Python中,我们可以用进程和协程实现同时做多件事情,就像你一边刷抖音,一边听音乐。 它们适用于不同的场景。
什么是进程?
你可以把进程想象成一家独立的工厂,它有自己的厂房(内存),自己的工人(资源)。如果一个工厂倒闭了,不会影响其他工厂。
- 特点
- 独立性强:每个进程都有自己独立的内存空间,互不干扰。 例如,你在两个不同的浏览器窗口打开网页,每个窗口就是一个独立的进程。
- 资源占用多:因为要独立的内存空间,所以开销比较大。 启动一个进程通常比启动一个线程慢10-100倍。
- 切换慢:就像你从一个工厂跑到另一个工厂,比较费时间。 进程切换的开销通常在微秒级别。
- 稳定性高:一个进程崩溃了,不会影响其他进程。 例如,一个浏览器窗口崩溃了,不会导致整个浏览器关闭。
- 适用场景:
- CPU密集型任务:需要大量计算的任务,比如视频编码、数据分析。 这类任务需要充分利用多核CPU的优势,每个进程在一个CPU核心上跑,速度非常快。
- 例子:你要用Python压缩一个很大的视频文件,可以把视频分成几段,每个段用一个进程来处理,这样可以充分利用电脑的多个CPU核心,比如4核CPU,理论上可以提速4倍。
- 代码示例
这段代码创建了3个独立的进程,每个进程执行from multiprocessing import Process import time, os def task(name): print(f'进程 {name} (PID: {os.getpid()}) 开始运行...') time.sleep(2) # 模拟耗时操作,比如复杂的数学计算 print(f'进程 {name} (PID: {os.getpid()}) 运行结束.') if __name__ == '__main__': processes = [] for i in range(3): # 创建3个进程 p = Process(target=task, args=(f'任务{i+1}',)) processes.append(p) p.start() for p in processes: p.join() # 等待所有子进程结束 print('所有进程运行完毕!')task函数。time.sleep(2)模拟了CPU密集型任务,例如复杂的数学计算。每个进程都在独立的CPU核心上运行,互不干扰。 运行结果显示每个进程的PID(进程ID),并且它们并发执行,总耗时接近2秒,而不是串行执行的6秒。
什么是协程?
协程就像一个工厂里的多个工人,他们共享同一个工厂的地盘(内存),但是他们很自觉,会主动让出时间给别人,避免一个人长时间占用资源。 协程是一种用户态的轻量级线程,由程序员自己调度。
- 特点:
- 轻量级:协程占用的资源很少,创建和切换速度非常快。 创建一个协程的开销通常比创建一个线程小100倍。
- 共享内存:所有协程共享同一个进程的内存空间,通信很方便。 但这也意味着需要注意线程安全问题,例如使用锁来避免数据竞争。
- 切换快:就像工厂里的工人换班,几乎不花时间。 协程切换的开销通常在纳秒级别。
- 适用场景:
- I/O密集型任务:需要频繁等待的任务,比如网络请求、读写文件。 这类任务CPU并不需要一直忙碌,大部分时间都在等待数据。协程可以在等待的时候切换到其他任务,提高效率。
- 例子:你写一个爬虫程序,需要从很多网站抓取数据。用协程可以让你在等待网站响应的时候,去抓取其他网站,而不用傻傻地等着。 如果用多线程,当线程数过多时,线程切换的开销会超过实际的抓取时间,导致效率下降。
- 代码示例
这段代码使用了import asyncio import time async def fetch_data(url): print(f"开始获取 {url}") # 模拟网络请求的延迟 await asyncio.sleep(1) # 模拟网络IO等待 print(f"{url} 获取完成") return f"来自 {url} 的数据" async def main(): urls = [ "https://www.example.com/page1", "https://www.example.com/page2", "https://www.example.com/page3", ] tasks = [fetch_data(url) for url in urls] results = await asyncio.gather(*tasks) for result in results: print(result) if __name__ == "__main__": start_time = time.time() asyncio.run(main()) end_time = time.time() print(f"总耗时: {end_time - start_time:.2f} 秒")asyncio库来创建和管理协程。async def fetch_data(url)定义了一个协程函数,模拟了网络请求的延迟。await asyncio.sleep(1)模拟了I/O等待,例如等待网络响应。asyncio.gather(*tasks)并发执行多个协程任务,总耗时接近1秒,而不是串行执行的3秒。
进程 vs 协程:对比表格
| 特性 | 进程 | 协程 |
|---|---|---|
| 资源占用 | 多 | 少 |
| 并行性 | 真正的并行(多核CPU) | 单线程并发(看似同时进行) |
| 切换 | 慢 (微秒级) | 快 (纳秒级) |
| 稳定性 | 高 | 相对较低 (需要注意线程安全) |
| 通信 | 进程间通信(IPC) | 共享内存 |
| 适用场景 | CPU密集型 | I/O密集型 |
| 典型应用 | 大规模并行计算 | 高并发网络服务 |
总结
- 如果你的任务需要大量计算,而且你的电脑是多核CPU,那就用进程。 例如,科学计算、视频编码。
- 如果你的任务需要频繁等待,比如网络请求,那就用协程。 例如,Web服务器、网络爬虫。
选择合适的工具,才能事半功倍! 进程适用于CPU密集型任务,可以充分利用多核CPU的优势;协程适用于I/O密集型任务,可以在等待I/O时切换到其他任务,提高效率。