Python使用multiprocessing多进程报错的一种情况解决方法

本文探讨了Python使用multiprocessing进行多进程执行时遇到的问题,包括权限冲突、网络访问超限等问题,并提出了通过保护进程创建过程来避免无限递归的解决方案。

问题

Python使用multiprocessing进行多进程执行是常常会报各种奇怪的错误。比如使用tushare时会反复拉起pro_api,全局变量访问次数超过限制,进程帧读取权限报错等等。

分析

注意到一个问题,在这种情况下无论是报什么错误,往往是因为权限冲突或者反复调用。这意味着很多不应该被重复调用的工具被多次重复调用了。

在multiprocessing的官方文档中,我找到了这样一条提示:

Functionality within this package requires that the __main__ module be importable by the children. This is covered in Programming guidelines however it is worth pointing out here. This means that some examples, such as the multiprocessing.pool.Pool examples will not work in the interactive interpreter.

也就是说,在交互式环境中,我们应当使用main保护进程创建过程。但是实际上在验证时,未必有效。

在Stack Overflow中,我找到了这样一条说明:

On some systems, multiprocessing has to spawn a new copy of python and import your module to get to the worker code. Anything at module level is executed again... including the parent code that creates the pool. This would be an infinite recursion except python detects the problem and gives you a handy tip.

 也就是说,这是因为multiprocessing会复制整个Python进程环境来保证子进程能够工作,然后重新执行所有模块级的任务,如果创建进程的过程不受保护,就会形成无限递归。

解决方法 

综合上述两点,做出一个基本猜测:由于multiprocessing会复制整个父进程来保证子进程能够工作,那么创建进程的过程必须受到main的保护以避免子进程的进程池创建过程调用创建进程池的方法。那么在任何时候保证只在main空间能创建进程池即可。代码如下:

if __name__ == "__main__":
    with mp.Pool(10) as pool:
        use_function(pool, *args, **kwargs)

评价

这里往往报错的不会是multiprocessing,而且往往不止一个地方报错,因此会比较有迷惑性。我这次,同时出现了读取权限冲突、网络访问超限、程序拉起出错等错误。上在出现多重错误的时候就应该能想到可能问题不出在所有执行过程了,所以我首先检查了multiprocessing,但还是错过了,后来排查了一圈回来还是multiprocessing的嫌疑最大,只有这个模块我使用的懒人方法,里头的创建过程对我而言是完全黑箱的。

<think> 在Python多进程编程中,使用`input()`函数可能会引发异常,因为子进程的标准输入(stdin)通常没有正确关联到主进程的控制台输入。这会导致`EOFError`或`OSError`。以下是解决方案: ### 问题分析 当在子进程中调用`input()`时,由于子进程的标准输入流没有正确重定向,它会立即遇到文件结束(EOF),从而引发`EOFError: EOF when reading a line`。 ### 解决方案 有以下几种方法解决多进程中的输入问题: #### 方法1:避免在子进程中使用`input()` 最佳实践是将所有用户交互放在主进程中,然后通过进程间通信(IPC)将数据传递给子进程。 ```python from multiprocessing import Process, Queue def worker(input_queue): """子进程从队列获取输入数据""" data = input_queue.get() print(f"子进程接收到: {data}") if __name__ == "__main__": # 主进程获取用户输入 user_input = input("主进程请输入: ") # 创建队列并传递数据 input_queue = Queue() p = Process(target=worker, args=(input_queue,)) p.start() # 将输入数据放入队列 input_queue.put(user_input) p.join() ``` #### 方法2:重定向子进程的标准输入(仅适用于类Unix系统) 在类Unix系统(Linux/macOS)中,可以使用`sys.stdin`重定向,但Windows不支持。 ```python import sys from multiprocessing import Process def safe_input(): """尝试安全读取输入""" try: return input("子进程请输入: ") except EOFError: return "子进程输入失败" if __name__ == "__main__": # 仅在Unix-like系统有效 if sys.platform not in ['linux', 'darwin']: print("警告:方法仅适用于类Unix系统") p = Process(target=safe_input) p.start() p.join() ``` #### 方法3:使用`multiprocessing.Manager`创建共享输入 通过Manager创建一个共享的输入变量,但输入操作仍需在主进程执行。 ```python from multiprocessing import Manager, Process def worker(shared_data): print("子进程读取:", shared_data['input']) if __name__ == "__main__": with Manager() as manager: shared_dict = manager.dict() # 主进程获取输入 shared_dict['input'] = input("主进程输入: ") p = Process(target=worker, args=(shared_dict,)) p.start() p.join() ``` ### 错误处理示例 当子进程意外调用`input()`时,如何捕获异常: ```python from multiprocessing import Process def risky_task(): try: data = input("子进程尝试输入: ") except EOFError as e: print(f"子进程捕获异常: {e}") data = "默认值" print("子进程继续执行...") if __name__ == "__main__": p = Process(target=risky_task) p.start() p.join() ``` ### 跨平台输入解决方案 对于需要从子进程获取用户输入的复杂场景,建议: 1. 使用主进程作为唯一输入点 2. 通过IPC传递输入指令 3. 使用网络套接字或命名管道实现跨进程输入 ```python # 示例:使用Queue传递输入指令 from multiprocessing import Process, Queue import time def interactive_worker(cmd_queue): while True: if not cmd_queue.empty(): command = cmd_queue.get() if command == "exit": break print(f"执行命令: {command}") time.sleep(1) if __name__ == "__main__": cmd_queue = Queue() p = Process(target=interactive_worker, args=(cmd_queue,)) p.start() while True: user_cmd = input("输入命令 (输入'exit'退出): ") cmd_queue.put(user_cmd) if user_cmd == "exit": break p.join() ``` ### 关键结论 1. **设计原则**:在多进程架构中,应将所有用户交互保留在主进程 2. **错误处理**:子进程中使用`input()`必须捕获`EOFError` 3. **跨平台限制**:Windows系统对子进程的标准输入有严格限制 §§相关问题§§: 1. 如何在Python多线程中使用`input()`函数? 2. 多进程间如何进行实时双向通信? 3. 如何通过管道(Pipe)实现进程间输入重定向? 4. 在Windows系统下多进程输入有哪些特殊限制? </think> # Python 多进程使用 `input()` 报错解决方案 在 Python 多进程环境中使用 `input()` 函数会引发 `EOFError`,这是因为子进程的标准输入(stdin)没有正确连接到控制台。下面是详细分析和解决方案: ## 问题分析及解决方案 ### 问题重现代码 ```python from multiprocessing import Process def child_process(): try: # 在子进程中尝试获取用户输入 user_input = input("子进程请输入: ") print(f"您输入了: {user_input}") except EOFError as e: print(f"错误发生: {e}") print("原因: 子进程的标准输入(stdin)未连接到控制台") if __name__ == "__main__": p = Process(target=child_process) p.start() p.join() ``` ### 输出结果 ``` 错误发生: EOF when reading a line 原因: 子进程的标准输入(stdin)未连接到控制台 ``` ## 解决方案 ### 方案1: 主进程处理所有输入(推荐) 将所有用户输入集中在主进程中处理,然后通过IPC传递给子进程: ```python from multiprocessing import Process, Queue import time def worker(input_queue): """子进程工作函数""" print("子进程开始工作...") # 等待并获取来自主进程的输入 while input_queue.empty(): time.sleep(0.1) user_input = input_queue.get() print(f"子进程收到: {user_input}") # 处理输入... if __name__ == "__main__": # 创建通信队列 input_queue = Queue() # 启动子进程 p = Process(target=worker, args=(input_queue,)) p.start() # 主进程获取用户输入 user_input = input("主进程请输入: ") # 将输入发送给子进程 input_queue.put(user_input) p.join() print("程序结束") ``` ### 方案2: 使用 `multiprocessing.Manager` 共享输入 通过管理器创建共享字典存储输入数据: ```python from multiprocessing import Process, Manager import time def shared_input_worker(shared_data): """等待共享输入的子进程""" print("子进程等待输入...") # 轮询等待输入 while not shared_data['has_input']: time.sleep(0.1) print(f"子进程收到: {shared_data['user_input']}") if __name__ == "__main__": with Manager() as manager: # 创建共享字典 shared_dict = manager.dict() shared_dict['has_input'] = False shared_dict['user_input'] = "" p = Process(target=shared_input_worker, args=(shared_dict,)) p.start() # 主进程获取输入 user_input = input("主进程请输入: ") # 更新共享数据 shared_dict['user_input'] = user_input shared_dict['has_input'] = True p.join() print("程序结束") ``` ### 方案3: 使用 `threading` 处理输入(适用于简单场景) 在主进程中使用线程处理输入,然后传递给子进程: ```python from multiprocessing import Process, Queue import threading def input_thread(input_queue): """专用的输入线程""" while True: try: data = input("请输入 (输入 'exit' 退出): ") if data.lower() == 'exit': input_queue.put('exit') break input_queue.put(data) except EOFError: break def processor(input_queue): """处理输入的子进程""" while True: if not input_queue.empty(): data = input_queue.get() if data == 'exit': print("子进程终止") break print(f"处理中: {data}") if __name__ == "__main__": input_queue = Queue() # 启动输入处理线程 input_thread = threading.Thread(target=input_thread, args=(input_queue,)) input_thread.daemon = True input_thread.start() # 启动处理子进程 p = Process(target=processor, args=(input_queue,)) p.start() # 等待进程结束 input_thread.join() p.join() ``` ## 替代 `input()` 的跨进程输入方案 ### 使用网络套接字实现跨进程输入 ```python from multiprocessing import Process import socket def socket_server(): """接收输入的套接字服务器""" server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(('localhost', 12345)) server.listen(1) print("输入服务器已启动,等待连接...") conn, addr = server.accept() print(f"连接来自: {addr}") with conn: while True: data = conn.recv(1024).decode() if not data or data == 'exit': break print(f"服务器收到: {data}") def socket_client(): """发送输入的客户端""" client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect(('localhost', 12345)) while True: try: msg = input("客户端请输入: ") client.send(msg.encode()) if msg == 'exit': break except EOFError: break client.close() if __name__ == "__main__": server_process = Process(target=socket_server) client_process = Process(target=socket_client) server_process.start() client_process.start() client_process.join() server_process.terminate() # 终止服务器进程 ``` ## 关键结论 1. **根本原因**:子进程的stdin未连接到控制台 2. **最佳实践**: - 所有用户输入在主进程处理 - 使用队列(Queue)或共享内存传递数据 - 避免在子进程中直接使用`input()` 3. **替代方案**: - 使用网络套接字进行进程间通信 - 使用文件或数据库作为中间存储 4. **错误处理**: ```python try: user_input = input("请输入: ") except EOFError: print("警告:在子进程中无法直接获取用户输入") # 回退到其他输入方案... ```
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值