Python并发编程:多进程(multiprocessing模块)

简介: 在处理CPU密集型任务时,Python的全局解释器锁(GIL)可能会成为瓶颈。为了充分利用多核CPU的性能,可以使用Python的multiprocessing模块来实现多进程编程。与多线程不同,多进程可以绕过GIL,使得每个进程在自己的独立内存空间中运行,从而实现真正的并行计算。

在处理CPU密集型任务时,Python的全局解释器锁(GIL)可能会成为瓶颈。为了充分利用多核CPU的性能,可以使用Python的multiprocessing模块来实现多进程编程。与多线程不同,多进程可以绕过GIL,使得每个进程在自己的独立内存空间中运行,从而实现真正的并行计算。

一、什么是multiprocessing模块

multiprocessing模块是Python标准库中的一个模块,它提供了一个类似于threading模块的接口,用于创建和管理进程。通过这个模块,我们可以轻松地创建进程池、管理进程间的通信和同步等。

1.1 模块导入

要使用multiprocessing模块,首先需要导入它:

import multiprocessing

1.2 创建进程

使用multiprocessing模块,我们可以通过创建一个Process对象来启动一个新进程。Process对象接受一个目标函数和可选的参数,当调用start()方法时,目标函数将在新进程中执行。

from multiprocessing import Process
def worker():
    print("Worker process")
if __name__ == "__main__":
    p = Process(target=worker)
    p.start()
    p.join()

二、进程间通信

在多进程编程中,进程之间的通信和数据共享是一个常见需求。multiprocessing模块提供了几种机制来实现进程间的通信,包括队列(Queue)、管道(Pipe)和共享内存(Value和Array)。

2.1 队列(Queue)

队列是一种FIFO(先进先出)数据结构,适用于进程间的消息传递。multiprocessing.Queue提供了一个线程和进程安全的队列接口。

from multiprocessing import Process, Queue
def worker(q):
    q.put("Hello from worker")
if __name__ == "__main__":
    q = Queue()
    p = Process(target=worker, args=(q,))
    p.start()
    print(q.get())
    p.join()

2.2 管道(Pipe)

管道提供了一个双向的通信通道,允许两个进程之间相互发送消息。

from multiprocessing import Process, Pipe
def worker(conn):
    conn.send("Hello from worker")
    conn.close()
if __name__ == "__main__":
    parent_conn, child_conn = Pipe()
    p = Process(target=worker, args=(child_conn,))
    p.start()
    print(parent_conn.recv())
    p.join()

2.3 共享内存(Value和Array)

共享内存允许多个进程直接访问共享的变量。multiprocessing.Value和multiprocessing.Array提供了这样的共享内存接口。

from multiprocessing import Process, Value, Array
def worker(num, arr):
    num.value = 42
    for i in range(len(arr)):
        arr[i] = -arr[i]
if __name__ == "__main__":
    num = Value('i', 0)
    arr = Array('i', range(10))
    p = Process(target=worker, args=(num, arr))
    p.start()
    p.join()
    print(num.value)
    print(arr[:])

三、进程池(Pool)

multiprocessing.Pool提供了一个方便的接口,用于管理进程池。进程池允许我们一次创建多个进程,并通过池中的进程并行执行多个任务。

3.1 创建进程池

我们可以使用Pool对象创建一个进程池,并通过apply、map等方法将任务分配给进程池中的进程。

from multiprocessing import Pool
def worker(x):
    return x * x
if __name__ == "__main__":
    with Pool(4) as p:
        print(p.map(worker, range(10)))

3.2 进程池的异步操作

进程池还支持异步操作,可以通过apply_async和map_async方法提交异步任务,并通过get方法获取结果。

from multiprocessing import Pool
def worker(x):
    return x * x
if __name__ == "__main__":
    with Pool(4) as p:
        result = p.apply_async(worker, (10,))
        print(result.get())
        multiple_results = [p.apply_async(worker, (i,)) for i in range(10)]
        print([res.get() for res in multiple_results])

四、锁和同步

在多进程编程中,多个进程可能会同时访问共享资源,导致数据竞争和不一致的问题。multiprocessing模块提供了多种同步机制,包括锁(Lock)、信号量(Semaphore)和条件变量(Condition)等。

4.1 锁(Lock)

锁用于确保在同一时间只有一个进程能够访问共享资源。

from multiprocessing import Process, Lock
def worker(lock, num):
    with lock:
        print(f"Worker {num}")
if __name__ == "__main__":
    lock = Lock()
    processes = [Process(target=worker, args=(lock, i)) for i in range(5)]
    for p in processes:
        p.start()
    for p in processes:
        p.join()

4.2 信号量(Semaphore)

信号量用于控制对共享资源的访问数量,适用于允许多个进程同时访问共享资源的情况。

from multiprocessing import Process, Semaphore
def worker(semaphore, num):
    with semaphore:
        print(f"Worker {num}")
if __name__ == "__main__":
    semaphore = Semaphore(2)
    processes = [Process(target=worker, args=(semaphore, i)) for i in range(5)]
    for p in processes:
        p.start()
    for p in processes:
        p.join()

4.3 条件变量(Condition)

条件变量允许进程等待特定条件发生后再继续执行,适用于需要进程间协作的场景。

from multiprocessing import Process, Condition
import time
def worker(cond, num):
    with cond:
        cond.wait()
        print(f"Worker {num}")
if __name__ == "__main__":
    condition = Condition()
    processes = [Process(target=worker, args=(condition, i)) for i in range(5)]
    for p in processes:
        p.start()
    time.sleep(2)
    with condition:
        condition.notify_all()
    for p in processes:
        p.join()

五、综合详细的例子

下面是一个综合详细的例子,模拟一个多进程的网页爬虫,使用进程池来管理多个爬虫进程,并确保爬取的数据不会重复。

网页爬虫示例

import multiprocessing
import requests
from bs4 import BeautifulSoup
import time
class WebCrawler:
    def __init__(self, urls, num_processes):
        self.urls = urls
        self.num_processes = num_processes
        self.visited_urls = set()
        self.lock = multiprocessing.Lock()
    def fetch_url(self, url):
        try:
            response = requests.get(url)
            return response.text
        except requests.RequestException as e:
            print(f"Failed to fetch {url}: {e}")
            return None
    def parse_html(self, html):
        soup = BeautifulSoup(html, 'html.parser')
        return [a['href'] for a in soup.find_all('a', href=True)]
    def crawl(self, url):
        if url in self.visited_urls:
            return []
        
        html = self.fetch_url(url)
        if html is None:
            return []
        new_urls = self.parse_html(html)
        with self.lock:
            self.visited_urls.add(url)
        return new_urls
    def start_crawling(self):
        with multiprocessing.Pool(self.num_processes) as pool:
            new_urls = pool.map(self.crawl, self.urls)
        return new_urls
if __name__ == "__main__":
    start_time = time.time()
    urls = [
        'https://example.com',
        'https://example.org',
        'https://example.net'
    ]
    crawler = WebCrawler(urls, num_processes=4)
    new_urls = crawler.start_crawling()
    print("New URLs:", new_urls)
    print("Crawling completed in", time.time() - start_time, "seconds")

运行结果

New URLs: [['https://example.com/page1', 'https://example.com/page2'], [], []]
Crawling completed in 2.5 seconds

六、总结

本文详细介绍了Python的multiprocessing模块,包括进程的创建、进程间通信、进程池的使用、进程同步等,并通过多个示例展示了如何在实际项目中应用这些技术。通过学习这些内容,您应该能够熟练掌握Python中的多进程编程,提高编写并发程序的能力。


多进程编程可以显著提高程序的并行计算性能,但也带来了新的挑战和问题。在使用多进程时,需要注意避免数据竞争、合理使用同步机制,并尽量通过进程池来管理和控制进程。


作者:Rjdeng

链接:https://juejin.cn/post/7397622646631251977

相关文章
|
1月前
|
Python
Python Internet 模块
Python Internet 模块。
127 74
|
2月前
|
算法 数据安全/隐私保护 开发者
马特赛特旋转算法:Python的随机模块背后的力量
马特赛特旋转算法是Python `random`模块的核心,由松本真和西村拓士于1997年提出。它基于线性反馈移位寄存器,具有超长周期和高维均匀性,适用于模拟、密码学等领域。Python中通过设置种子值初始化状态数组,经状态更新和输出提取生成随机数,代码简单高效。
131 63
|
23天前
|
Python
[oeasy]python057_如何删除print函数_dunder_builtins_系统内建模块
本文介绍了如何删除Python中的`print`函数,并探讨了系统内建模块`__builtins__`的作用。主要内容包括: 1. **回忆上次内容**:上次提到使用下划线避免命名冲突。 2. **双下划线变量**:解释了双下划线(如`__name__`、`__doc__`、`__builtins__`)是系统定义的标识符,具有特殊含义。
27 3
|
2月前
|
持续交付 Python
如何在Python中自动解决模块和包的依赖冲突?
完全自动解决所有依赖冲突可能并不总是可行,特别是在复杂的项目中。有时候仍然需要人工干预和判断。自动解决的方法主要是提供辅助和便捷,但不能完全替代人工的分析和决策😉。
|
26天前
|
数据采集 消息中间件 Java
python并发编程:什么是并发编程?python对并发编程有哪些支持?
并发编程能够显著提升程序的效率和响应速度。例如,网络爬虫通过并发下载将耗时从1小时缩短至20分钟;APP页面加载时间从3秒优化到200毫秒。Python支持多线程、多进程、异步I/O和协程等并发编程方式,适用于不同场景。线程通信方式包括共享变量、消息传递和同步机制,如Lock、Queue等。Python的并发编程特性使其在处理大规模数据和高并发访问时表现出色,成为许多领域的首选语言。
|
8月前
|
安全 Python
Python中的并发编程:多线程与多进程技术探究
本文将深入探讨Python中的并发编程技术,重点介绍多线程和多进程两种并发处理方式的原理、应用场景及优缺点,并结合实例分析如何在Python中实现并发编程,以提高程序的性能和效率。
|
Java 开发者 Python
< Python全景系列-5 > 解锁Python并发编程:多线程和多进程的神秘面纱揭晓
< Python全景系列-5 > 解锁Python并发编程:多线程和多进程的神秘面纱揭晓
73 0
|
8月前
|
数据采集 数据库 C++
python并发编程:并发编程中是选择多线程呢?还是多进程呢?还是多协程呢?
python并发编程:并发编程中是选择多线程呢?还是多进程呢?还是多协程呢?
87 0
|
2月前
|
并行计算 数据处理 调度
Python中的并发编程:探索多线程与多进程的奥秘####
本文深入探讨了Python中并发编程的两种主要方式——多线程与多进程,通过对比分析它们的工作原理、适用场景及性能差异,揭示了在不同应用需求下如何合理选择并发模型。文章首先简述了并发编程的基本概念,随后详细阐述了Python中多线程与多进程的实现机制,包括GIL(全局解释器锁)对多线程的影响以及多进程的独立内存空间特性。最后,通过实例演示了如何在Python项目中有效利用多线程和多进程提升程序性能。 ####
|
6月前
|
消息中间件 安全 数据处理
Python中的并发编程:理解多线程与多进程的区别与应用
在Python编程中,理解并发编程是提高程序性能和响应速度的关键。本文将深入探讨多线程和多进程的区别、适用场景及实际应用,帮助开发者更好地利用Python进行并发编程。

热门文章

最新文章