Python 并发新境界:探索 `multiprocessing` 模块的无限可能

news/2024/10/5 2:52:34/

引言

随着硬件技术的发展,多核处理器已经成为标准配置。这意味着我们的计算机拥有执行多个任务的能力。然而,默认情况下,Python程序由于全局解释器锁(GIL)的存在,并不能充分利用这些核心资源。这就引出了multiprocessing模块的重要性——它通过创建独立进程来绕过GIL限制,从而实现真正的并行计算。

multiprocessing模块的应用场景非常广泛,从简单的文件处理到复杂的科学计算,甚至是Web爬虫等都可以见到它的身影。接下来,我们将逐步了解如何利用这一强大工具来优化我们的Python程序。

基础语法介绍

核心概念

  • 进程:一个独立的程序实例,有自己的内存空间,可以并行执行。
  • Pool:用于管理一组工作进程,提供了一个简单的方式来分发任务给不同的进程。
  • Queue:进程间通信的一种方式,允许不同进程之间传递对象。
  • Lock/Semaphore:控制多个线程/进程对共享资源的访问,防止竞态条件。

基本语法规则

创建进程的基本语法如下所示:

python">from multiprocessing import Processdef worker():print('I am a worker')if __name__ == '__main__':p = Process(target=worker)p.start()p.join()

这里,我们定义了一个名为worker的函数,然后通过Process类创建了一个新的进程对象p,并将worker作为目标函数传入。最后调用start()方法启动进程,并通过join()等待其完成。

基础实例

假设我们需要对一批图片进行处理(例如调整大小),如果单靠主线程执行将会非常耗时。这时,我们可以借助multiprocessing模块来加速这一过程。

python">from multiprocessing import Pool
import os, time, randomdef long_time_task(name):print(f'Run task {name} (pid: {os.getpid()})')start = time.time()time.sleep(random.random() * 3)end = time.time()print(f'Task {name} runs {end-start:.2f} seconds')if __name__ == '__main__':print('Parent process %s.' % os.getpid())p = Pool(4)for i in range(5):p.apply_async(long_time_task, args=(i,))print('Waiting for all subprocesses done...')p.close()p.join()print('All subprocesses done.')

在这个例子中,我们创建了一个包含4个子进程的Pool对象,并使用apply_async方法异步地将任务分发给它们。注意closejoin的使用,前者告诉Pool不再接受新的任务,后者则会阻塞主进程直到所有子进程完成。

进阶实例

当涉及到更复杂的应用场景时,如大规模数据分析或分布式系统开发,multiprocessing模块同样能够发挥巨大作用。比如下面这个示例展示了如何使用Manager来创建共享对象,从而实现进程间的数据交换。

python">from multiprocessing import Manager, Processdef f(d, l):d[1] = '1'd['2'] = 2d[0.25] = Nonel.reverse()if __name__ == '__main__':manager = Manager()d = manager.dict()l = manager.list(range(10))p_list = []for i in range(10):p = Process(target=f, args=(d, l))p.start()p_list.append(p)for res in p_list:res.join()print(d)print(l)

这里我们通过Manager创建了两个可以被多个进程共享的对象:字典d和列表l。每个进程都会修改这些共享对象的内容,最终结果表明所有修改都被正确地同步到了所有进程中。

实战案例

在实际工作中,multiprocessing模块往往与其他库结合使用以解决特定问题。以一个典型的Web爬虫项目为例,我们不仅需要抓取网页数据,还需要对其进行解析和存储。这无疑是一个耗时的过程,特别是当目标网站较多时。此时,我们可以考虑将整个流程拆分成几个阶段,并行处理每一部分以提高效率。

python">from bs4 import BeautifulSoup
from urllib.request import urlopen
from multiprocessing import Process, Queuedef crawl(url, q):response = urlopen(url)html = response.read().decode('utf-8')soup = BeautifulSoup(html, features='html.parser')q.put(soup)def parse(q):while True:try:item = q.get_nowait()except Empty:breakelse:# 对item进行解析操作...if __name__ == '__main__':urls = ['http://example.com/page%d' % page for page in range(1, 11)]q = Queue()crawlers = [Process(target=crawl, args=(url, q)) for url in urls]parsers = [Process(target=parse, args=(q,)) for _ in range(5)]for crawler in crawlers:crawler.start()for parser in parsers:parser.start()for crawler in crawlers:crawler.join()for parser in parsers:parser.join()

上述代码首先定义了两个函数crawlparse分别负责抓取和解析工作。接着我们创建了一个队列q用于存放中间结果,并根据需求启动相应数量的爬虫进程和解析进程。通过这种方式,整个爬虫系统的吞吐量得到了显著提升。

扩展讨论

尽管multiprocessing模块提供了丰富的功能,但我们在实际使用过程中仍需注意以下几点:

  • 进程间通信:虽然multiprocessing模块内置了多种通信机制,但在设计时仍然需要谨慎选择合适的方法,以保证数据传输的高效性和安全性。
  • 资源共享:由于每个进程都有自己独立的内存空间,因此对于那些需要在多个进程中共享的数据,必须通过特殊手段(如Manager提供的共享对象)来实现。
  • 异常处理:与普通线程相比,进程之间的异常传播更加复杂,因此在编写多进程程序时应当充分考虑到这一点,并做好相应的容错设计。
  • 死锁问题:当多个进程同时尝试访问同一资源时,可能会导致死锁现象发生。为了避免这种情况,通常需要合理安排各进程的执行顺序或引入锁定机制。
  • 性能考量:虽然多进程可以有效提升程序运行速度,但也并非万能药。特别是在处理小型任务时,频繁创建和销毁进程反而会增加额外开销。因此,在决定是否使用multiprocessing之前,最好先进行性能测试。

http://www.ppmy.cn/news/1534710.html

相关文章

五子棋双人对战项目(1)——WebSocket介绍

目录 一、项目介绍 如何实现实时同步对局? 二、WebSocket 1、什么是WebSocket? 2、WebSocket的报文格式 opcode payload len payload data 3、WebSocket握手过程 4、WebSocket代码的简单编写 三、WebSocket 和 HTTP的关系 1、相同点&#xf…

OJ在线评测系统 在Linux虚拟机搭建Docker 概念 入门 安装

Docker的基本概念 为什么要用docker容器技术 为了提升系统的安全性 把不同的程序和宿主机进行隔离 使得某个程序 应用的执行不会影响到系统本身 docker技术可以实现程序和宿主机的隔离 容器可以理解成对一系列应用程序、服务和环境的封装 从而把程序运行在一个隔离的 封闭…

招联2025校招内推

【投递方式】 直接扫下方二维码,或点击内推官网https://wecruit.hotjob.cn/SU61025e262f9d247b98e0a2c2/mc/position/campus,使用内推码 igcefb 投递) 【招聘岗位】 后台开发 前端开发 数据开发 数据运营 算法开发 技术运维 软件测试 产品策…

【Android 14源码分析】WMS-窗口显示-第一步:addWindow

忽然有一天,我想要做一件事:去代码中去验证那些曾经被“灌输”的理论。                                                                                  – 服装…

08.useInterval

在 React 应用中,实现定时器功能通常需要使用 setInterval() 和 clearInterval(),这可能会导致代码复杂和难以维护。useInterval 钩子提供了一种声明式的方法来实现定时器功能,使得定时器的管理更加简单和直观。这个自定义钩子不仅简化了定时器的使用,还解决了一些常见的定…

【Nacos架构 原理】内核设计之Nacos通信通道

文章目录 Nacos通信通道 (长链接)现状背景场景分析配置服务 长链接核心诉求功能性诉求负载均衡连接生命周期 Nacos通信通道 (长链接) 现状背景 Nacos 1.X 版本 Config/Naming 模块各自的推送通道都是按照自己的设计模型来实现的…

基于微信小程序的四六级词汇+ssm(lw+演示+源码+运行)

摘 要 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,四六级词汇小程序被用户普遍使用,为方便用户能…

STM32正交编码器的结构与工作原理

1. 引言 正交编码器是一种广泛应用于位置和速度反馈控制的传感器。在日常生活中,我们可以将其类比为我们的手指如何控制一个旋钮。例如,当我们转动音量旋钮时,旋转的角度和方向直接影响音量的大小。正如旋钮需要反馈来调整音量,正…