目录
要点
- 并发和并行是计算中任务执行的基本原则,两者都有其独特的特征。
- 并发性可以实现高效的资源利用并提高应用程序的响应能力,而并行性对于最佳性能和可扩展性至关重要。
- Python 提供了处理并发的选项,例如使用 asyncio 的线程和异步编程,以及使用多处理模块的并行性。
并发和并行是两种可让您同时运行多个程序的技术。 Python 有多种并发和并行处理任务的选项,这可能会令人困惑。
探索可用于在 Python 中正确实现并发和并行性的工具和库,以及它们的不同之处。
了解并发和并行性
并发和并行是指计算中任务执行的两个基本原则。 每个都有其独特的特点。
并发和并行的重要性
计算中对并发性和并行性的需求怎么强调都不为过。 这就是为什么这些技术很重要:
Python 中的并发
您可以使用 asyncio 库的线程和异步编程在 Python 中实现并发。
Python 中的线程
线程是一种 Python 并发机制,允许您在单个进程中创建和管理任务。 线程适用于某些类型的任务,特别是那些受 I/O 限制且可以从并发执行中受益的任务。
Python的线程模块 提供用于创建和管理线程的高级接口。 虽然 GIL(全局解释器锁)在真正的并行性方面限制了线程,但它们仍然可以通过有效地交错任务来实现并发。
下面的代码显示了使用线程实现并发的示例。 它使用Python请求库发送HTTP请求,这是一种常见的I/O阻塞任务。 它还使用 time 模块来计算执行时间。
import requests
import time
import threadingurls = [
'https://www.google.com',
'https://www.wikipedia.org',
'https://www.makeuseof.com',
]
def download_url(url):
response = requests.get(url)
print(f"Downloaded {url} - Status Code: {response.status_code}")
start_time = time.time()for url in urls:
download_url(url)end_time = time.time()
print(f"Sequential download took {end_time - start_time:.2f} seconds\n")
start_time = time.time()
threads = []for url in urls:
thread = threading.Thread(target=download_url, args=(url,))
thread.start()
threads.append(thread)
for thread in threads:
thread.join()end_time = time.time()
print(f"Threaded download took {end_time - start_time:.2f} seconds")
运行该程序,您应该会看到线程请求比顺序请求快了多少。 尽管差异只有几分之一秒,但在使用线程执行 I/O 密集型任务时,您可以清楚地感受到性能的提高。
使用 Asyncio 进行异步编程
异步 提供一个事件循环来管理称为协程的异步任务。 协程是可以暂停和恢复的函数,因此非常适合 I/O 密集型任务。 该库对于任务涉及等待外部资源(例如网络请求)的场景特别有用。
您可以修改前面的请求发送示例以使用 asyncio:
import asyncio
import aiohttp
import timeurls = [
'https://www.google.com',
'https://www.wikipedia.org',
'https://www.makeuseof.com',
]
async def download_url(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
content = await response.text()
print(f"Downloaded {url} - Status Code: {response.status}")
async def main():
tasks = [download_url(url) for url in urls]
await asyncio.gather(*tasks)start_time = time.time()
asyncio.run(main())end_time = time.time()
print(f"Asyncio download took {end_time - start_time:.2f} seconds")
使用该代码,您可以使用 asyncio 同时下载网页并利用异步 I/O 操作。 对于 I/O 密集型任务来说,这比线程化更有效。
Python 中的并行性
您可以使用以下方式实现并行性 Python 的多处理模块,这使您可以充分利用多核处理器。
Python 中的多处理
Python 的多处理模块提供了一种通过创建单独的进程来实现并行性的方法,每个进程都有自己的 Python 解释器和内存空间。 这有效地绕过了全局解释器锁(GIL),使其适合 CPU 密集型任务。
import requests
import multiprocessing
import timeurls = [
'https://www.google.com',
'https://www.wikipedia.org',
'https://www.makeuseof.com',
]
def download_url(url):
response = requests.get(url)
print(f"Downloaded {url} - Status Code: {response.status_code}")def main():
num_processes = len(urls)
pool = multiprocessing.Pool(processes=num_processes)start_time = time.time()
pool.map(download_url, urls)
end_time = time.time()
pool.close()
pool.join()print(f"Multiprocessing download took {end_time-start_time:.2f} seconds")
main()
在此示例中,多处理生成多个进程,允许 download_url 函数并行运行。
何时使用并发或并行
并发和并行之间的选择取决于任务的性质和可用的硬件资源。
在处理 I/O 密集型任务(例如读取和写入文件或发出网络请求)以及需要考虑内存限制时,可以使用并发。
当您的 CPU 密集型任务可以从真正的并行性中受益,并且任务之间具有强大的隔离性(其中一个任务的失败不应影响其他任务)时,请使用多处理。
利用并发和并行性
并行性和并发性是提高 Python 代码响应能力和性能的有效方法。 了解这些概念之间的差异并选择最有效的策略非常重要。
Python 提供了您所需的工具和模块,让您的代码通过并发性或并行性变得更加高效,无论您使用的是 CPU 密集型进程还是 I/O 密集型进程。