【问题标题】:Python Multiprocessing makes system unresponsivePython 多处理使系统无响应
【发布时间】:2020-02-28 17:30:51
【问题描述】:

我正在处理多个数据集(使用自定义 python 对象封装并在磁盘上序列化),其中包含一系列操作,每个操作大约需要 10/15 秒。我已经将这些操作封装在一个进程上,我正在调用 160 个进程来进行调度。

我设置了Semaphore(6),因为我有一个 8 核 CPU。

问题是在进程 80 左右,RAM 已满,Swap 也已满,系统完全没有响应。

我也努力使用垃圾收集器,但没有任何区别。

它开始正常运行,当时间过去时,系统完全冻结。这是崩溃前htop 的屏幕截图: Swap is at 5GB but it gets at 8GB before crash

我的系统是: - Ubuntu 18.04 LTS - i7 2600 4 核(8 线程) - GTX 1060 3GB - 内存 DDR3 8GB

代码如下:

import multiprocessing as mp
import sys
import time
from pathlib import Path

import gc


def main():
    all_args_list = load_datasets()

    processes = []
    queue_res = mp.Queue()
    cpu_lim = mp.Semaphore(mp.cpu_count() - 2) # Use 6 of my 8 cores

    for i, some_args in enumerate(all_args_list):

        p = mp.Process(target=process_routine, args=(some_args, queue_res, cpu_lim))
        processes.append(p)

    for process in processes:
        process.start()

    for process in processes:
        process.join()

    results = [queue_res.get() for p in processes]

    save(results)


def process_routine(some_args, queue_obj, cpu_lim):
    cpu_lim.acquire()

    # Do my operations ....
    process_result_dict = my_operations(some_args)

    # Save process result
    res_obj.put(process_result_dict)

    gc.collect()
    cpu_lim.release()


if __name__ == '__main__':
    main()

我应该如何进行以防止 python 劫持所有内存? 为什么 RAM 使用量会随着时间的推移而增加?垃圾收集器不应该收集旧对象吗?

感谢您的宝贵时间!

编辑

我将多个进程更改为一个池。问题是现在它只单独运行每个进程,使用apply_async()apply()

pool = mp.Pool(processes=mp.cpu_count())
results = [pool.apply_async(process_routine, args=(my_args,)) for id, my_args in enumerate(datasets)]

【问题讨论】:

  • 是的,消耗所有系统资源通常会降低您的计算机响应速度。问题是如何减少资源使用?如果他们都同时工作,那么 180 个 procs 是一大堆。
  • 在大多数情况下,您希望池大小等于 CPU 数量。
  • @jordanm,是的,问题是为什么交换和内存耗尽(不应该 gc 清理旧对象)以及如何防止它完全系统崩溃
  • 如果您使用的是Pool,请去掉Semaphore,并将池的大小显式设置为mp.cpu_count() - 2apply 预计单独运行; apply_async 不是,但我们不知道您为每个任务做了多少实际工作;如果它很小,并且序列化的参数很昂贵,那将消除您所有的节省。
  • 至于 gc 不工作:分叉时的 GC 比不 GC 更糟糕;它触及了大部分引用计数,导致大部分写时复制内存被立即复制。我建议changing your start method to 'forkserver',删除gc.collect,如果这还不够,设置maxtasksperchild=SOMENUMBER,以便间歇性收集子进程并从头开始(这是确保释放所有内存的唯一方法) .

标签: python python-multiprocessing python-multithreading


【解决方案1】:

您应该尝试使用大小等于系统上 CPU 数量的池作为起点,而不是启动 160 个进程。看看ProcessPoolExecutor。当它可以为您管理进程池/资源时,无需执行您自己的信号量逻辑。

【讨论】:

  • 我已经将其更改为池 multiprocessing.Pool 但现在它似乎没有并行运行进程
  • 你能发布你完全更新的代码吗?你也更新了process_routine 吗?除非您有特定的理由使用multiprocessing,否则请按照文档中的示例尝试ProcessPoolExecutor
  • @ajomc 是什么给了你这样的印象?你能分享你的程序的修改版本吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-05-13
  • 1970-01-01
  • 2015-07-06
  • 1970-01-01
  • 2015-01-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多