Python爬虫中生产者消费者模式进程意外退出的排查与修复

Miles
Miles
发布时间:2026-10-16| 8人浏览过
原创

本文分析Python爬虫中使用多进程生产者-消费者模式时,因未正确调用join()导致消费者进程过早终止的问题。通过对比错误与正确的代码结构,指出必须等待生产者完成并调用消费者join(),同时需向队列发送结束标志,确保所有任务被处理完毕。

问题背景与现象

在开发爬虫程序时,通常采用生产者与消费者模式,并利用Queue作为两者之间的同步队列来传递数据。

然而在实际运行中,程序经常瞬间退出,即使设置断点或添加打印语句也无法捕获中间状态。这表明进程在预期之前就已经终止,但具体原因起初并不明确。

原因分析与解决思路

起初怀疑是进程代码存在崩溃点导致提前退出,经过多次排查未发现明显错误。在仔细审查主模块中消费者和生产者的启动逻辑后,发现问题根源在于对start()和join()方法的使用不当。关键点在于:必须对消费者进程执行join()操作,否则消费者会在完成所有工作前被强制终止。

错误代码示例

以下是导致进程过早退出的错误实现方式:

queue = multiprocessing.JoinableQueue()
consumer = PageContentConsumer(queue)
consumer.start()

producer = PageContentProducer(queue)
producer.start()

# 想通过queue的join()方法确保queue中的元素都被处理完毕
# 但从实际运行看,消费者进程还没来得及处理就退出了
queue.join()

正确代码示例

修正后的正确实现方式如下:

queue = multiprocessing.JoinableQueue()
consumer = PageContentConsumer(queue)
consumer.start()

producer = PageContentProducer(queue)
producer.start()
# 需要执行producer.join(),确保生产者进程能够持续执行
producer.join()

# 需要执行consumer.join(),确保消费者进程有时间进行处理
consumer.join()

# 通过queue的join()方法确保queue中的元素都被处理完毕, 这一步可选,因为真实代码里放了队列完成标志
queue.join()

生产者进程实现

生产者进程的核心代码示例:

class PageContentProducer(multiprocessing.Process):
    def __init__(self, page_list:list, output_queue:multiprocessing.JoinableQueue):
        multiprocessing.Process.__init__(self)
        self.daemon = True
        self.page_list = page_list
        self.content_list = []  # 用于保存汇总信息,没有什么实际作用
        self.output_queue = output_queue
    
    def run(self):
        '''
        向队列中加入每一篇文章
        '''
        self.visit_all_page_to_get_content()
        
    def visit_all_page_to_get_content(self):
        '''
        使用线程池处理所有的page, 并从每一页上提取所有的文章content
        '''
        ...

消费者进程实现

消费者进程的核心代码示例:

class PageContentConsumer(multiprocessing.Process):
    def __init__(self, dir, input_queue:multiprocessing.JoinableQueue):
        multiprocessing.Process.__init__(self)
        self.daemon = True
        self.input_queue = input_queue
        self.dir = dir
        
    def run(self):
        while True:
            try:
                content = self.input_queue.get()
                if content is None:
                    # 如果收到结束标志, 就退出当前任务
                    break
                self.content_worker_func(self.dir, content)
                print(f"已处理: {content['title']}")
                # 发出信号通知任务完成
                self.input_queue.task_done()
            except Exception as e:
                print(repr(e))
                
    def content_worker_func(self, dir, content):
        '''
        主要工作函数
        '''
        ...

主模块同步逻辑

主模块中进程启动与同步的完整逻辑示意:

if __name__ == '__main__':
    page_list = [xxxx]
    
    queue = multiprocessing.JoinableQueue()
    
    consumer_num = os.cpu_count()
    consumers = []
    
    for i in range(0, consumer_num):
        consumers.append(PageContentConsumer(dir, queue))
    
    for i in range(0, consumer_num):
        consumers[i].start()
    
    producer = PageContentProducer(page_list, queue)
    producer.start()
    producer.join()
    
    # 在队列上放置标志,发出完成信号, 有几个消费者,就需要放置多少个标志
    for i in range(0, consumer_num):
        queue.put(None)
        
    # 等待消费者进程关闭
    for i in range(0, consumer_num):
        consumers[i].join()

以上就是关于Python爬虫中使用生产者-消费者模式时进程过早退出问题的解决方案。更多相关技术细节可参考本站其他文章或继续浏览后续内容,感谢支持。

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。