Python 中的并行处理
简介
在当今快节奏的数字环境中,有效完成计算困难的工作对于开发人员和数据科学家至关重要。幸运的是,Python 凭借其适应性和广泛的生态系统提供了强大的并行处理能力。我们可以通过将困难问题分解为可以同时执行的较小、更易于管理的活动来获得巨大的性能改进。
Python 的并行处理功能使我们能够利用可用的计算机资源更快、更有效地开展网页抓取、科学模拟和数据分析等活动。我们将在这篇文章中开始 Python 并行处理的旅程。我们将研究多种方法,包括多处理、异步编程和多线程,并学习如何有效地使用它们来绕过我们系统中的性能障碍。加入我们,让我们充分发挥 Python 并行处理的全部威力,并达到性能和生产力的新高度。
了解并行处理
将作业拆分为较小的子任务并在多个处理器或核心上同时运行它们称为并行处理。并行处理可以通过有效利用可用的计算资源大大减少程序的总执行时间。异步编程、多处理和多线程只是 Python 提供的几种并行处理方法。
Python 中的多线程
使用多线程方法,单个进程内的多个线程可以同时运行,同时共享相同的内存。使用 Python 的线程模块可以轻松实现多线程。但是,由于全局解释器锁 (GIL),Python 中的多线程不一定会导致 CPU 密集型操作的速度加快,它只允许一个线程一次执行 Python 字节码。但是,多线程对于 I/O 密集型作业非常有用,因为它允许线程在等待 I/O 操作完成时运行其他操作。
让我们看一个使用多线程下载许多网页的示例:
示例
import threading import requests def download_page(url): response = requests.get(url) print(f"Downloaded {url}") urls = [ "https://example.com", "https://google.com", "https://openai.com" ] threads = [] for url in urls: thread = threading.Thread(target=download_page, args=(url,)) thread.start() threads.append(thread) for thread in threads: thread.join()
输出
Downloaded https://example.com Downloaded https://google.com Downloaded https://openai.com
由于上述代码片段,多个下载可以同时进行,每个 URL 在其自己的线程中下载。 join() 函数确保主线程等到每个线程完成后再继续。
Python 中的多处理
与多线程相反,多处理通过使用多个进程(每个进程都有自己的内存空间)提供真正的并行性。 Python 的多处理模块提供了用于实现多处理的高级接口。 多处理适用于 CPU 密集型活动,因为每个进程都在不同的 Python 解释器中运行,从而避免了 GIL 多线程限制。
以下代码中使用了多处理。一旦池类生成工作进程池,map() 方法就会将负担分摊到可用进程中。结果列表是结果的集合。
考虑下面的例子,我们使用多重处理来对列表中的每个整数进行平方:
示例
import multiprocessing def square(number): return number ** 2 numbers = [1, 2, 3, 4, 5] with multiprocessing.Pool() as pool: results = pool.map(square, numbers) print(results)
输出
[1, 4, 9, 16, 25]
Python 中的异步编程
通过利用非阻塞操作,异步编程可以高效执行 I/O 绑定进程。借助 asyncio 包,协程、事件循环和 Future 都可用于在 Python 中创建异步代码。随着在线应用程序和 API 的普及,异步编程变得越来越重要。
以下代码示例中的 fetch_page() 协程利用 aiohttp 异步获取网页。main() 方法生成作业列表,然后使用 asyncio.gather() 同时执行这些作业。要等待任务完成并接收结果,请使用 await 关键字。
让我们看一个使用 asyncio 和 aiohttp 异步获取许多网页的示例:
示例
import asyncio import aiohttp async def fetch_page(url): async with aiohttp.ClientSession() as session: async with session.get(url) as response: return await response.text() async def main(): urls = [ "https://example.com", "https://google.com", "https://openai.com" ] tasks = [fetch_page(url) for url in urls] pages = await asyncio.gather(*tasks) print(pages) asyncio.run(main())
输出
['<!doctype html>\n<html>\n<head>\n <title>Example Domain</title>\n\n <meta charset="utf-8" />\n <meta http-equiv="Content-type"content="text/html; charset=utf-8" />\n <meta name="viewport" content="width=device-width, initialscale=1" />\n <style type="text/css">\n body {\n background-color: #f0f0f2;\n margin: 0;\n padding: 0;\n font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;\n \n }\n div {\n width: 600px;\n margin: 5em auto;\n padding: 50px;\n background-color: #fff;\n border-radius: 1em;\n }\n a:link, a:visited {\n color: #38488f;\n text-decoration: none;\n }\n @media (maxwidth: 700px) {\n body {\n background-color: #fff;\n }\n div {\n width: auto;\n margin: 0 auto;\n border-radius: 0;\n padding: 1em;\n }\n }\n </style> \n</head>\n\n<body>\n<div>\n <h1>Example Domain</h1>\n <p>This domain is for use in illustrative examples in documents. You may use this\n domain in literature without prior coordination or asking for permission.</p>\n <p><a href="https://www.iana.org/domains/example">More information...</a></p>\n</div>\n</body>\n</html>', '<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="en"><head><meta content="Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for." name="description"><meta content="noodp" name="robots"><meta content="text/html; charset=UTF-8" http-equiv="Content-Type"><meta content="/logos/doodles/2021/mom- and-dad-6116550989716480.2-law.gif" itemprop="image"><link href="/logos/doodles/2021/mom-and-dad-6116550989716480.2-law.gif" rel="icon" type="image/gif"><title>Google</title><script nonce="sJwM0Ptp5a/whzxPtTD8Yw==">(function(){window.google={kEI:'cmKgYY37A7 K09QPhzKuACw',kEXPI:'1354557,1354612,1354620,1354954,1355090,1355493,13556 83,3700267,4029815,4031109,4032677,4036527,4038022,4043492,4045841,4048347,4 048490,4052469,4055589,4056520,4057177,4057696,4060329,4060798,4061854,4062 531,4064696,406 '
选择正确的方法
Python 的并行处理技术因手头任务的具体情况而异。以下是一些帮助您做出明智决定的指南:
对于 I/O 密集型活动,其中大部分执行时间都花在等待输入/输出操作上,多线程是合适的。它非常适合下载文件、使用 API 和操作文件等任务。由于 Python 的全局解释器锁 (GIL),多线程可能不会显著加快 CPU 密集型活动的速度。
另一方面,多处理非常适合涉及密集计算的 CPU 密集型任务。它通过利用多个进程(每个进程都有自己的内存空间)实现真正的并行性,并绕过 GIL 的限制。但是,它在内存消耗和进程间通信方面会产生额外的开销。
对于涉及网络操作的 I/O 密集型活动,使用 asyncio 等库执行的异步编程很有用。它利用非阻塞 I/O 操作,因此作业可以继续进行,而无需等待每个操作完成。此方法有效地管理多个并发连接,使其适用于网络服务器开发、Web API 交互和 Web 抓取。异步编程最大限度地减少了 I/O 操作等待时间,确保了响应能力和可扩展性。
结论
Python 的并行处理能力提供了提高需要复杂计算的任务效率的机会。无论您选择使用多线程、多处理还是异步编程,Python 都提供了有效利用并发的必要工具和模块。通过理解活动的性质并选择适当的技术,您可以最大限度地发挥并行处理的优势并缩短执行时间。所以继续探索并充分利用 Python 的并行性来创建更快、更高效的应用程序。