【问题标题】:Multiprocessing in Python with read-only shared memory?具有只读共享内存的 Python 中的多处理?
【发布时间】:2013-10-14 17:59:42
【问题描述】:

我有一个单线程 Python 程序,我想修改它以利用它运行的服务器上的所有 32 个处理器。正如我所设想的,每个工作进程都会从队列中接收其作业并将其输出提交到队列。然而,为了完成它的工作,每个工作进程都需要对复杂的内存数据结构进行只读访问——许多 GB 的字典和相互链接的对象。在 python 中,有没有一种简单的方法来共享这个数据结构,而不需要为每个工作进程复制它?

谢谢。

【问题讨论】:

  • 你使用的是 Python 的 CPython 实现吗?
  • 是的,现在我正在使用 CPython,在 Linux 上运行。我想到了使用多处理模块。

标签: python multithreading multiprocessing shared-memory


【解决方案1】:

如果您使用的是 Python 的 CPython(或 PyPy)实现,那么 global interpreter lock (GIL) 将阻止多个线程一次对 Python 对象进行操作。

因此,如果您使用这样的实现,则需要使用多个进程而不是多个线程来利用您的 32 个处理器。

您可以使用标准库的multiprocessing 或concurrent.futures 模块来生成工作进程。还有many third-party options。 Doug Hellman's tutorial 是对多处理模块的一个很好的介绍。

由于您只需要对数据结构进行只读访问,因此如果您在生成进程之前将复杂数据结构分配给全局变量,那么所有进程都可以访问该全局变量变量。

当你生成一个进程时,来自调用模块的全局变量会被复制到生成的进程中。但是,在具有copy-on-write 的 Linux 上,生成的进程使用完全相同的数据结构,因此不需要额外的内存。只有当一个进程修改数据结构时,它才会被复制到一个新的位置。

在 Windows 上,由于没有fork,每个生成的进程都会调用 python 并重新导入调用模块,因此每个进程都需要内存来存储自己的庞大数据结构的单独副本。必须有其他方式在 Windows 上共享数据结构,但我不知道细节。 (编辑:POSH may be a solution to the shared-memory problem,但我自己没有尝试过。)

【讨论】:

  • 太好了,因为我使用的是 Linux。在阅读了多处理包的文档后,我认为我必须使用multiprocessing.Value 或multiprocessing.Array 来共享内存。但是从您的 cmets 看来,这听起来只有在我想写入共享内存时才有必要。对吗?
  • @Jeff:对。 some code here 证明了使用写时复制,大对象可以共享用于只读目的,而不需要额外的内存。
【解决方案2】:

要添加上面 unutbu 答案的演示,这里的代码显示它实际上是 COW 共享内存(CPython 3.6,Mac OS)

main_shared.py

import multiprocessing
from time import sleep


my_global = None


def test():
    global my_global
    read_only_secs = 3
    while read_only_secs > 0:
        sleep(1)
        print(f'child proc global: {my_global} at {hex(id(my_global))}')
        read_only_secs -= 1
    print('child proc writing to copy-on-write...')
    my_global = 'something else'
    while True:
        sleep(1)
        print(f'child proc global: {my_global} at {hex(id(my_global))}')


def set_func():
    global my_global
    my_global = [{'hi': 1, 'bye': 'foo'}]

if __name__ == "__main__":
    print(f'main proc global: {my_global} at {hex(id(my_global))}')
    set_func()
    print(f'main proc global: {my_global} at {hex(id(my_global))}')
    p1 = multiprocessing.Process(target=test)
    p1.start()

    while True:
        sleep(1)
        print(f'main proc global: {my_global} at {hex(id(my_global))}')

输出

$ python main_shared.py 
main proc global: None at 0x101b509f8
main proc global: [{'hi': 1, 'bye': 'foo'}] at 0x102341708
child proc global: [{'hi': 1, 'bye': 'foo'}] at 0x102341708
main proc global: [{'hi': 1, 'bye': 'foo'}] at 0x102341708
child proc global: [{'hi': 1, 'bye': 'foo'}] at 0x102341708
main proc global: [{'hi': 1, 'bye': 'foo'}] at 0x102341708
child proc global: [{'hi': 1, 'bye': 'foo'}] at 0x102341708
child proc writing to copy-on-write...
main proc global: [{'hi': 1, 'bye': 'foo'}] at 0x102341708
child proc global: something else at 0x1022ea3b0
main proc global: [{'hi': 1, 'bye': 'foo'}] at 0x102341708
child proc global: something else at 0x1022ea3b0
main proc global: [{'hi': 1, 'bye': 'foo'}] at 0x102341708
child proc global: something else at 0x1022ea3b0
main proc global: [{'hi': 1, 'bye': 'foo'}] at 0x102341708

【讨论】:

    猜你喜欢
    • 2021-10-15
    • 1970-01-01
    • 2017-01-02
    • 2015-03-15
    • 2013-08-19
    • 2012-12-16
    • 2018-10-20
    • 2021-12-01
    • 1970-01-01
    相关资源
    最近更新 更多