查看: 156|回复: 3

Python多线程与多进程的实现方法:并发爬虫与GIL影响分析

[复制链接]
发表于 9 小时前 | 显示全部楼层 |阅读模式
在Python中处理大量任务时,并发编程可以显著提升程序执行效率。但很多开发者对多线程和多进程的适用场景存在误解,尤其是对全局解释器锁(GIL)的影响认识不足。本文通过具体代码示例,详解两种并发方式的实现、差异及最佳实践。

## 一、并发概念

并发(Concurrency)是指程序同时处理多个任务的能力。Python提供两种主流并发方式:

- 多线程(threading):在同一个进程内创建多个执行线程,共享进程内存空间。
- 多进程(multiprocessing):创建多个独立进程,每个进程拥有独立内存空间。

## 二、多线程实现

多线程适用于I/O密集型任务,例如网络请求、文件读写等。以下代码演示如何创建并启动两个线程:
  1. import threading
  2. import time
  3. def print_numbers():
  4.     for i in range(5):
  5.         time.sleep(1)
  6.         print(f'Thread {threading.current_thread().name}: {i}')
  7. t1 = threading.Thread(target=print_numbers, name='Thread-1')
  8. t2 = threading.Thread(target=print_numbers, name='Thread-2')
  9. t1.start()
  10. t2.start()
  11. t1.join()
  12. t2.join()
复制代码

通过threading.Thread()创建线程对象,指定target为函数,start()启动线程,join()等待线程结束。由于线程共享进程内全局变量,通信方便但需注意同步问题(如使用锁)。

## 三、多进程实现

多进程适合CPU密集型任务,例如图像处理、数学计算。每个进程拥有独立的Python解释器和GIL,因此可以充分利用多核CPU:
  1. from multiprocessing import Process
  2. import time
  3. def calculate():
  4.     print('Starting calculation...')
  5.     time.sleep(3)
  6.     print('Calculation complete')
  7. p1 = Process(target=calculate)
  8. p2 = Process(target=calculate)
  9. p1.start()
  10. p2.start()
  11. p1.join()
  12. p2.join()
复制代码

Process的用法类似Thread,但启动开销更大(需要创建新进程),进程间通信需使用Queue、Pipe或Value/Array等机制。

## 四、多线程 vs 多进程对比

| 特性       | 多线程                 | 多进程                 |
|------------|------------------------|------------------------|
| 内存共享   | 是                     | 否(需独立通信)       |
| 启动开销   | 小                     | 大                     |
| 上下文切换 | 快                     | 慢                     |
| 适用场景   | I/O密集型              | CPU密集型              |
| GIL影响    | 受GIL限制,无法并行CPU | 不受GIL影响,可并行CPU |

## 五、实战案例:并发爬虫

网络请求是典型的I/O密集型任务,使用ThreadPoolExecutor可以轻松实现并发抓取:
  1. import requests
  2. from concurrent.futures import ThreadPoolExecutor
  3. def fetch_url(url):
  4.     response = requests.get(url)
  5.     return response.text
  6. urls = ['https://example.com', 'https://juejin.cn', 'https://zhihu.com']
  7. with ThreadPoolExecutor(max_workers=5) as executor:
  8.     results = list(executor.map(fetch_url, urls))
  9. print(results)
复制代码

这里max_workers=5表示同时最多5个线程,executor.map()会依次返回每个URL的响应内容。注意:requests.get()会阻塞线程,但多线程切换使整体等待时间大大缩短。

## 六、最佳实践

1. 了解GIL限制:Python的全局解释器锁(GIL)使得即使在多线程中,同一时刻只有一个线程执行Python字节码。所以纯CPU计算任务(如循环累加)使用多线程并不能加速,此时应使用多进程。
2. 线程同步:多线程共享全局变量时需要使用threading.Lock保护临界区,避免竞态条件。
3. 合理设置线程池/进程池大小:对于I/O密集型,线程数可设置为CPU核心数的数倍(如2~4倍);对于CPU密集型,进程数通常等于CPU核心数。
4. 监控资源:避免创建过多线程导致系统资源耗尽。可使用queue设置任务上限,或使用信号量控制并发数。
5. 选择合适的数据结构:例如使用queue.Queue线程安全队列来在线程间传递任务,或使用multiprocessing.Manager实现进程间共享状态。

## 七、总结

- 多线程(threading) → I/O密集型任务(网络请求、文件操作)
- 多进程(multiprocessing) → CPU密集型任务(计算、图像处理)
- 对写入密集型场景,考虑使用concurrent.futures模块的ThreadPoolExecutor/ProcessPoolExecutor简化代码。
- 始终关注GIL对并发性能的影响,根据任务类型选择正确的并发方案。

通过合理运用上述技术,可以显著提升Python程序的吞吐量和响应速度。
回复

使用道具 举报

发表于 9 小时前 | 显示全部楼层

Re: Python多线程与多进程的实现方法:并发爬虫与GIL影响分析

感谢楼主的详细分享!对新手来说,多线程和多进程的区分确实容易混淆,尤其是GIL的影响。你列举的对比表格和实际爬虫案例很直观,ThreadPoolExecutor的用法也很实用。 想请教一个小问题:在实战爬虫部分,如果用多进程代替多线程来处理I/O密集型的网络请求,是不是会由于进程启动开销大而得不偿失?还是说有特殊场景下多进程更适合爬虫?另外,对于需要共享爬取状态(比如去重URL集合)的情况,楼主推荐用multiprocessing.Manager还是其他方式?期待你的进一步解答!
回复 支持 反对

使用道具 举报

发表于 7 小时前 | 显示全部楼层

Re: Python多线程与多进程的实现方法:并发爬虫与GIL影响分析

楼主这篇总结得很清晰,尤其是对比表格和实战爬虫的例子,对新手理解多线程和多进程的区别很有帮助。关于GIL的影响,很多人一开始确实容易踩坑,以为多线程就能直接利用多核,结果发现CPU密集任务反而更慢。你提到的最佳实践里“合理设置线程池/进程池大小”这一点,我在实际项目中也深有体会——I/O任务线程数设得太高反而会增加切换开销,有时候调优比代码本身更花时间。另外,你用的`ThreadPoolExecutor`配合`requests`做并发爬虫是很经典的写法,不过如果目标网站有反爬,可能还需要加上延时或代理池,这又是另一个话题了。总之,感谢分享,收藏了。
回复 支持 反对

使用道具 举报

发表于 5 小时前 | 显示全部楼层

Re: Python多线程与多进程的实现方法:并发爬虫与GIL影响分析

感谢楼主分享,总结得非常清晰!代码示例和对比表格很直观,特别是实战爬虫部分直接用ThreadPoolExecutor,对新手很友好。想请教一下:对于既包含大量I/O又有少量CPU计算的任务(比如爬取后解析JSON),您一般会用多线程还是多进程,或者结合使用?
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-6-16 20:21 , Processed in 0.023984 second(s), 17 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部