用multiprocessing.Queue自己编写进程池实现DataLoader(父子进程通信)

本文介绍如何利用multiprocessing库中的Queue实现父子进程间的通信,并基于此原理自定义DataLoader,用于数据预处理。此外,还探讨了解决多进程环境下主进程被终止后遗留子进程的问题。

目录

1. 用Queue写进程池从而实现父子进程通信

2. 自己编写DataLoader

3. 关于多进程杀掉主进程会有子进程残留的问题


multiprocess.Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递, 底层使用管道pipe,同步信号量和互斥锁实现。

multiprocess库提供了pool进程池,直接实现了多进程之间的通信,pool的使用场景也很多,这里不做介绍。但pool不能实现父子进程之间的通信,要想实现父子通信,需要自己用Queue写一个进程池,通过创建子队列和父队列来进行父子通信。这个用途就更广泛了,比如想要开多个子进程帮你处理一些数据或文件,最后把结果都收回到主进程,这也就是自己编写DataLoader,很多实际项目中需要自己写一个数据预处理代码,所以要自己重写DataLoader。

本文先讲如何实现父子进程通信,之后就很容易实现DataLoader了。


1. 用Queue写进程池从而实现父子进程通信

思路其实很简单,先分别创建子队列父队列,然后在主进程中把文件放入子队列,多个子进程各自从子队列中取文件并进行处理,再把各自处理好的数据放入父队列,主进程等所有文件处理完后从父队列取处理好的数据。

这里比较需要注意的是用None作为退出信号,因为如果是用Queue的get()函数(默认block=False)则会在队列为空时阻塞等待而不退出;如果用get_nowait()也即get(block=True),则会在队列为空时报出异常;设置成当队列为空则退出也是不合理的。因此可以考虑用None作为退出信号,由主进程在文件都放入子队列后发出。

这里省略了具体处理data的函数。由于比较容易理解所以直接上代码。

from multiprocessing import Process, Queue


def data_preprocess(file, args):
    # preprocess data
    pass

def preprocess_module(queue_in, queue_res, args):
    while True:
        file = queue.get()
        # exit if getting terminating signal
        if file is None:
            break
        instance = data_preprocess(file, args)
        queue_res.put(instance)
    # put None into queue_res as terminating signal
    queue_res.put(None)


if __name__ == '__main__':
    nthread 
Traceback (most recent call last): File "<string>", line 1, in <module> File "D:\anaconda\envs\FirTest\lib\multiprocessing\spawn.py", line 116, in spawn_main exitcode = _main(fd, parent_sentinel) File "D:\anaconda\envs\FirTest\lib\multiprocessing\spawn.py", line 125, in _main prepare(preparation_data) File "D:\anaconda\envs\FirTest\lib\multiprocessing\spawn.py", line 236, in prepare _fixup_main_from_path(data['init_main_from_path']) File "D:\anaconda\envs\FirTest\lib\multiprocessing\spawn.py", line 287, in _fixup_main_from_path main_content = runpy.run_path(main_path, File "D:\anaconda\envs\FirTest\lib\runpy.py", line 288, in run_path return _run_module_code(code, init_globals, run_name, File "D:\anaconda\envs\FirTest\lib\runpy.py", line 97, in _run_module_code _run_code(code, mod_globals, init_globals, File "D:\anaconda\envs\FirTest\lib\runpy.py", line 87, in _run_code exec(code, run_globals) File "D:\PythonProject\Classified networks\train.py", line 8, in <module> from CNN_TRAIN import criterion File "D:\PythonProject\Classified networks\CNN_TRAIN.py", line 71, in <module> for images, labels in train_loader: File "D:\anaconda\envs\FirTest\lib\site-packages\torch\utils\data\dataloader.py", line 484, in __iter__ return self._get_iterator() File "D:\anaconda\envs\FirTest\lib\site-packages\torch\utils\data\dataloader.py", line 415, in _get_iterator return _MultiProcessingDataLoaderIter(self) File "D:\anaconda\envs\FirTest\lib\site-packages\torch\utils\data\dataloader.py", line 1138, in __init__ w.start() File "D:\anaconda\envs\FirTest\lib\multiprocessing\process.py", line 121, in start self._popen = self._Popen(self) File "D:\anaconda\envs\FirTest\lib\multiprocessing\context.py", line 224, in _Popen return _default_context.get_context().Process._Popen(process_obj) File "D:\anaconda\envs\FirTest\lib\multiprocessing\context.py", line 327, in _Popen return Popen(process_obj) Fil
06-10
<think>我们使用Pythonmultiprocessing模块来创建多进程,并使用Queue实现进程间通信。具体步骤:1.导入multiprocessing模块中的Process和Queue。2.定义一个函数作为子进程的执行目标,该函数会从队列中获取数据,处理后再将结果放入队列。3.创建Queue实例,用于进程间传递数据。4.创建多个Process实例,每个子进程都使用同一个队列。5.启动所有子进程。6.主进程向队列中放入数据。7.主进程等待子进程处理完成,并从队列中获取结果。注意:队列是进程安全的,多个进程可以安全地访问同一个队列。下面是一个示例代码,其中主进程向队列中放入10个数字,然后两个子进程分别从队列中取出数字,计算平方,再放入另一个结果队列中。最后主进程从结果队列中取出所有结果并打印。</think>### 使用 `multiprocessing.Process` 和 `Queue` 实现多进程通信Python 中,`multiprocessing.Process` 用于创建子进程,`Queue` 提供线程/进程安全的 FIFO 队列,实现进程间数据交换。以下是详细实现: #### 完整代码示例 ```python import multiprocessing import time def worker(task_queue, result_queue): """子进程工作函数:从任务队列取数据,处理后放入结果队列""" while True: # 阻塞式获取任务 task = task_queue.get() # 终止信号 if task is None: break # 模拟耗时计算(例如平方计算) result = task ** 2 time.sleep(0.5) # 模拟耗时 # 结果放入队列 result_queue.put((task, result)) print(f"Process {multiprocessing.current_process().name} processed: {task}") if __name__ == "__main__": # 创建队列(任务队列和结果队列) task_queue = multiprocessing.Queue() result_queue = multiprocessing.Queue() # 创建进程列表 processes = [] num_workers = 3 # 3个工作进程 # 启动工作进程 for i in range(num_workers): p = multiprocessing.Process( target=worker, args=(task_queue, result_queue), name=f"Worker-{i+1}" ) p.start() processes.append(p) # 向任务队列添加数据 tasks = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] for task in tasks: task_queue.put(task) # 添加终止信号(每个进程一个None) for _ in range(num_workers): task_queue.put(None) # 等待所有进程结束 for p in processes: p.join() # 从结果队列获取数据 print("\nResults:") while not result_queue.empty(): task, result = result_queue.get() print(f"{task}^2 = {result}") ``` #### 关键机制解释 1. **进程创建 (`Process`)** - `target=worker`:子进程执行的函数 - `args=(task_queue, result_queue)`:传递队列对象 - `p.start()`:启动子进程(非阻塞) - `p.join()`:主进程等待子进程结束 2. **进程间通信 (`Queue`)** - **`task_queue.put(item)`**:主进程向任务队列添加数据 - **`task_queue.get()`**:子进程阻塞获取任务 - **`result_queue.put(item)`**:子进程回传结果 - 队列自动处理进程间同步和锁机制 3. **优雅终止设计** - 向每个子进程发送 `None` 作为终止信号 - 子进程收到 `None` 时退出循环 - 避免 `while not queue.empty()` 的竞态条件 4. **队列特性** - 先进先出 (FIFO) - 线程/进程安全(内部使用锁和信号量) - 支持任意 Python 对象(需可序列化) #### 输出示例 ``` Process Worker-1 processed: 1 Process Worker-2 processed: 2 Process Worker-3 processed: 3 Process Worker-1 processed: 4 ...(其他任务处理)... Results: 1^2 = 1 2^2 = 4 3^2 = 9 4^2 = 16 ...(其他结果)... ``` #### 注意事项 1. **跨平台兼容性**:使用 `if __name__ == "__main__"` 避免 Windows 系统下的运行时错误 2. **队列阻塞**:`get()` 默认阻塞直到有数据,可用 `get_nowait()` 非阻塞版本 3. **对象序列化**:通过队列传递的对象必须可被 `pickle` 序列化 4. **资源释放**:确保进程正确终止,避免僵尸进程 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值