运行 Python 中主进程的并行进程保存结果

Run a parallel process saving results from a main process in Python

我有一个函数可以为任务列表创建一些结果。我想将结果即时保存到 1) 与保存到附加到 results_list 相比释放内存和 2) 在出现错误的情况下获得第一部分的结果。

这是一个非常简短的示例代码:

for task in task_list:
    result = do_awesome_stuff_to_task(task)
    save_nice_results_to_db(result)  # Send this job to another process and let the main process continue

有没有办法让主进程为 task_list 中的每个任务创建结果,并且每次创建结果时将其发送到另一个 processor/thread 以保存它,因此主循环可以继续而不等待缓慢的保存过程?

我看过多处理,但这似乎主要是为了加快 task_list 上的循环,而不是让辅助子进程执行其他部分的工作。我也研究过 asyncio,但这似乎主要用于 I/O.

总而言之,我正在寻找一种让主进程在 task_list 上循环的方法。对于每个完成的任务,我想将结果发送到另一个子流程以保存结果。请注意,do_awesome_stuff_to_task 比保存过程快得多,因此,主循环将在保存第一个任务之前完成多个任务。我想到了两种解决方法:

  1. 使用多个子进程保存
  2. 保存每 xx 次迭代 - save_results 规模没问题,所以也许保存过程可以在主循环连续时一次保存 xx 次迭代?

这可能与 Python 有关吗?在哪里看以及要考虑哪些关键因素?

感谢所有帮助。

如果不进行测试,很难知道什么会更快,但这里有一些关于如何选择要做什么的想法。

如果 save_nice_results_to_db 由于正在将数据写入磁盘或网络而变慢,请确保您尚未达到硬件的最大写入速度。根据另一端的服务器,网络流量有时可以从一次打开多个端口到 read/write 中获益匪浅,只要您保持在总网络传输速度(mac 接口以及您的互联网服务提供商)。 SSD 可以从一次启动多个 reads/writes 中看到一些有限的好处,但太多会损害性能。当试图一次做不止一件事时,HDD 几乎普遍较慢。一切都更有效率 reading/writing 一次更大的块。

multiprocessing 通常必须使用 pickle 在父进程和子进程之间传输数据,因为它们不共享内存。这有很高的开销,所以如果 result 是一个大对象,您可能会浪费更多的时间来将数据发送到子进程的额外开销,而不是通过任何类型的并发可以节省的时间。 (强调可能。总是自己测试)。从 3.8 开始,添加了 shared_memory 模块,它可能更高效,但灵活性和易用性要差得多。

threading 得益于所有线程共享内存,因此在线程之间“发送”数据的传输开销为零。然而,由于 GIL(全局解释器锁),Python 个线程无法并发执行字节码,因此无法利用多个 CPU 个内核来提高计算速度。这是因为 python 本身有很多部分不是线程安全的。用 c 编写的特定函数可能会释放此锁以解决此问题并使用线程利用多个 cpu 内核,但一旦 returns 执行到 python 解释器,该锁将再次持有。通常涉及网络访问或文件 IO 的函数可以释放 GIL,因为解释器正在等待通常是线程安全的操作系统调用。 Numpy 等其他流行的库也在努力发布 GIL,同时在大型数组上执行复杂的数学运算。但是,您只能从 c/c++ 代码中释放 GIL,而不能从 python 本身释放 GIL。

asyncio 应该在这里特别提到,因为它是专门为并发 network/file 操作而设计的。它使用协程而不是线程(甚至比线程的开销更低,线程本身的开销比进程低得多)来排队一堆操作,然后使用操作系统调用等待它们中的任何一个完成(事件循环)。使用它还需要您的 do_awesome_stuff_to_task 在协程中发生,以便它与 save_nice_results_to_db.

同时发生

将每个 result 触发到要处理的线程的简单示例:

for task in task_list:
    result = do_awesome_stuff_to_task(task)
    threading.Thread(target=save_nice_results_to_db, args=(result,)).start()  # Send this job to another process and let the main process continue