深入理解python多进程编程

  • Post category:Linux

深入理解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)和共享内存(ValueArray)等。

下面以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函数。进程池会自动管理进程数,并按照顺序执行任务。最后,我们将所有子列表合并成一个有序列表。