深入理解Python多进程编程
Python拥有强大的多进程编程能力,可以让我们充分发挥计算机多核心的性能优势来加速任务执行。在本文中,我们将深入探讨Python多进程编程的相关知识,并给出两个示例,帮助您理解多进程的概念、使用方法及其注意事项。
多进程编程的概念
多进程是指同时运行多个进程,每个进程都有独立的内存空间和计算资源,互不干扰。多进程编程可以将一个任务分解成多个子任务,并且并行地运行这些子任务,从而提高程序执行效率。
Python中多进程编程的主要模块为multiprocessing
,通过该模块可以轻松地实现多进程编程。
多进程编程的使用方法
创建进程
要使用multiprocessing
模块创建一个进程,需要使用其Process
类。Process
类的构造函数接收一个可执行对象(一般是一个函数)作为参数,并开始执行该函数。下面是一个示例:
import multiprocessing
def func():
print('Process is running')
if __name__ == '__main__':
p = multiprocessing.Process(target=func)
p.start()
以上代码创建了一个新的进程,该进程运行func
函数。在调用start
方法后,进程就会开始执行func
函数。
进程间通信
多进程编程时,不同进程间需要进行数据交换和协同工作。为了实现这样的功能,Python提供了多种进程间通信方法,例如队列(Queue
)、管道(Pipe
)和共享内存(Value
和Array
)等。
下面以Queue
为例,演示如何在多个进程间传递数据:
import multiprocessing
def producer(queue):
for i in range(10):
queue.put(i)
queue.put(None)
def consumer(queue):
while True:
item = queue.get()
if item is None:
break
print(item)
if __name__ == '__main__':
queue = multiprocessing.Queue()
p1 = multiprocessing.Process(target=producer, args=(queue,))
p2 = multiprocessing.Process(target=consumer, args=(queue,))
p1.start()
p2.start()
p1.join()
p2.join()
在上面的例子中,我们创建了一个生产者和一个消费者进程,生产者向队列中不断放入数字,消费者从队列中取出数字并打印。运行该程序后,我们可以看到所有数字都被正确地输出了。
进程池
当需要创建多个进程时,可以使用进程池来管理多个进程。进程池可以在执行完一个进程后将其放回池子中,以便下一个进程使用。
下面是一个简单的进程池示例:
import multiprocessing
def func(n):
return n * n
if __name__ == '__main__':
with multiprocessing.Pool(processes=3) as pool:
result = [pool.apply_async(func, args=(i,)) for i in range(1, 11)]
output = [r.get() for r in result]
print(output)
以上代码创建了一个进程池,并使用apply_async
方法分配多个进程执行同一个函数func
。在pool.apply_async
方法中,我们传递了要执行的函数以及它的参数。该方法将返回一个异步结果对象。接着,我们使用get
方法来获取异步结果对象的返回结果。
注意事项
在使用多进程时,有一些需要注意的事项。下面是一些常见的问题:
- 多进程之间的数据是不共享的,因此需要采用特殊方法进行进程间通信。
- 在创建新进程前,需要调用
if __name__ == '__main__'
语句,以确保每个子进程在自己的空间中执行。 - 多进程编程时,程序可能会消耗更多的内存和CPU资源,需要仔细考虑。
示例一:爬虫
下面是一个简单的爬虫程序,使用多进程进行加速。在该程序中,我们创建了多个进程来同时抓取多个网页的信息。
import requests
from bs4 import BeautifulSoup
import multiprocessing
def get_page(url):
res = requests.get(url, timeout=10)
if res.status_code == 200:
soup = BeautifulSoup(res.text, features='html.parser')
return soup.title.string
def handler(url):
try:
title = get_page(url)
print(f'{url}: {title}')
except requests.exceptions.RequestException:
print(f'{url}: failed')
if __name__ == '__main__':
urls = ['https://www.google.com', 'https://www.baidu.com', 'https://www.microsoft.com', 'https://www.apple.com']
with multiprocessing.Pool() as p:
p.map(handler, urls)
在上面的例子中,我们使用get_page
函数获取网页标题,并定义了一个回调函数handler
。在handler
函数中,我们将抓取到的数据打印出来。
在主函数中,我们创建了一个进程池,并使用map
方法将多个URL传递给handler
函数。进程池会自动管理进程的数量,并按照顺序执行任务。
示例二:多进程排序
下面是一个简单的排序程序,使用多进程进行加速。在该程序中,我们把一个列表分成多个子列表,每个子列表使用一个进程进行排序,最后将所有子列表按照排序结果合并成一个有序列表。
import random
import multiprocessing
def merge_sort(array):
if len(array) > 1:
mid = len(array) // 2
left = array[:mid]
right = array[mid:]
merge_sort(left)
merge_sort(right)
i = j = k = 0
while i < len(left) and j < len(right):
if left[i] < right[j]:
array[k] = left[i]
i += 1
else:
array[k] = right[j]
j += 1
k += 1
while i < len(left):
array[k] = left[i]
i += 1
k += 1
while j < len(right):
array[k] = right[j]
j += 1
k += 1
def sort_process(array):
merge_sort(array)
return array
if __name__ == '__main__':
random_array = random.sample(range(1, 1000), 1000)
sub_arrays = [random_array[i:i+100] for i in range(0, len(random_array), 100)]
with multiprocessing.Pool() as p:
sub_sorted = p.map(sort_process, sub_arrays)
sorted_array = []
for sub in sub_sorted:
sorted_array.extend(sub)
merge_sort(sorted_array)
print(sorted_array)
在上面的例子中,我们使用merge_sort
函数进行归并排序,并定义了一个新的函数sort_process
,用于将归并排序运行在不同的进程中。在主函数中,我们首先将原始列表划分为多个子列表,并使用map
方法将子列表传递给sort_process
函数。进程池会自动管理进程数,并按照顺序执行任务。最后,我们将所有子列表合并成一个有序列表。