在Python的世界里,如果你想要提升程序运行效率,尤其是处理大量数据或执行耗时任务时,必然绕不开“并发”与“并行”这两个关键词。它们虽然经常被同时提及,但实际含义和应用场景却大相径庭。今天,我们将深入探讨这两者的区别,并通过剖析Python内置的multiprocessing模块,揭示如何利用并行编程技巧,让Python程序如虎添翼。
并发,简单来说,就是“同时做多件事”。它并不意味着所有事情都在同一时刻发生,而是指系统能够在多个任务之间快速切换,给用户造成“同时进行”的错觉。比如,你在浏览网页的同时听音乐,尽管CPU可能在同一时间只能处理一个任务,但通过高效的调度机制,让你感觉两者是同步进行的。
并行,则是真正意义上的“同时做多件事”。它依赖于硬件支持,如多核CPU或多台计算机,能够将任务分解成多个部分,分别在不同的处理器上独立执行。并行执行能够显著提高计算密集型任务的处理速度,充分利用硬件资源。
在Python中,实现并发编程的一个常见手段是使用多线程。以threading模块为例,我们可以通过创建Thread对象来启动一个新的线程:
import threadingdef thread_function(name): print(f"Thread {name}: starting") # 执行耗时操作... print(f"Thread {name}: finishing")# 创建并启动两个线程for i in range(2): t = threading.Thread(target=thread_function, args=(i,)) t.start()
然而,Python的多线程并发受到全局解释器锁(Global Interpreter Lock, GIL)的制约。GIL是为了保护内存安全而引入的一把“大锁”,它确保任何时候只有一个线程在执行Python字节码。这意味着在单个进程中,即使有多个线程,也无法实现真正的并行计算。对于CPU密集型任务,多线程并发往往无法带来性能提升。
为了解决GIL带来的限制,Python提供了multiprocessing模块,它利用操作系统提供的进程机制,允许我们在不同进程中并行执行任务,从而规避GIL的影响。每个进程都有自己的Python解释器和内存空间,可以在多核CPU上真正实现并行计算。
multiprocessing的核心是Process类,用于创建新进程:
from multiprocessing import Processdef long_running_task(): # 执行耗时操作...if __name__ == "__main__": p = Process(target=long_running_task) p.start() # 启动进程 p.join() # 等待进程结束
进程间通信是并行编程的重要环节。multiprocessing提供了多种方式:
对于大量相似任务的处理,可以使用Pool对象创建一个进程池,避免频繁创建销毁进程的开销:
from multiprocessing import Pooldef process_data(data): # 对data进行处理...if __name__ == "__main__": with Pool(4) as pool: # 创建包含4个进程的进程池 results = pool.map(process_data, data_list) # 将data_list中的每个元素分发给进程池中的进程处理
假设我们需要对一个大数组进行平方运算,可以利用Pool.map()方法实现并行计算:
import numpy as npfrom multiprocessing import Pooldef square(number): return number ** 2if __name__ == "__main__": data = np.random.randint(1, 100, size=100000) with Pool(4) as pool: squared_data = pool.map(square, data)
若需处理异步任务,如网络请求,可以结合concurrent.futures模块实现:
import concurrent.futuresfrom multiprocessing import Pooldef fetch_url(url): # 发送网络请求并返回结果...if __name__ == "__main__": with concurrent.futures.ProcessPoolExecutor(max_workers=4) as executor: with Pool(4) as pool: future_to_url = {executor.submit(fetch_url, url): url for url in url_list} for future in concurrent.futures.as_completed(future_to_url): url = future_to_url[future] try: data = future.result() # 处理数据... except Exception as exc: print(f"{url} generated an exception: {exc}")
为了协调多个进程间的协作,multiprocessing提供了多种同步原语:
当子进程发生异常或主动退出时,可以通过捕获Process对象的exitcode属性或注册Process对象的join()方法的回调函数进行处理。
Python并发与并行编程虽有区别,但都是提升程序效率的有效手段。理解并掌握multiprocessing模块,能帮助我们编写出高效、稳定的并行程序。在实践中,应注意合理选择并发模型,妥善处理进程间通信与同步问题,以及应对可能出现的子进程异常情况。通过不断实践与优化,你的Python程序将能在多核CPU上飞速奔跑,轻松应对各类复杂任务。
本文链接://www.dmpip.com//www.dmpip.com/showinfo-26-87494-0.htmlPython并发与并行:multiprocessing模块大揭秘
声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com