python为什么要创建返回值的新副本,而不是仅在多重处理中创建引用

时间:2019-04-17 04:20:42

标签: python multiprocessing

我正在处理程序中的内存泄漏问题。我发现它与多重处理有关,因此我提出了以下实验。

在实验中,函数f生成一个列表和一个元组,我将检查从函数返回后id是否保持不变。

程序返回值最有效的方法是返回引用,以防止为相同的对象分配内存。当SYNC = True时,结果显示来自内部的ID等于从外部接收的ID。

但是,当SYNC = False并且多处理程序加入该程序时,内部的ID将不再等于外部的ID。这表明程序已经创建了对象的额外副本。

这实际上引起了2个问题:
1.复制对象时浪费内存和计算能力 2.保留在池中的副本不会被垃圾收集(通过其他实验发现)

有人能告诉我python处理此问题的机制吗?在引入多处理后如何避免我的程序成为内存吞噬者?

from multiprocessing import Pool

SYNC = False

def f(start):
    l = [i for i in range(start, start+100)] # generate a list from start to start-1
    t = tuple(i for i in range(start, start+100)) # generate a list from start to start-1
    print('inner: {}'.format(id(l)))
    print('inner: {}'.format(id(t)))
    return l, t

def iterate(it):
    for l, t in it:
        print('outer: {}'.format(id(l)))
        print('outer: {}'.format(id(t)))

pool = Pool(4)
inputs = [i for i in range(4)]

gen_sync = (f(start) for start in inputs)
gen_async = pool.imap(f, inputs, chunksize=4)

if SYNC:
    print('start testing sync')
    iterate(gen_sync)
else:
    print('start testing async')
    iterate(gen_async)

SYNC = True

  

开始测试同步
  内部:139905123267144
  内部:23185048
  外部:139905123267144
  外部:23185048
  内部:139905123249544
  内部:23186776
  外部:139905123249544
  外部:23186776
  内部:139905123267144
  内部:23187640
  外部:139905123267144
  外部:23187640
  内部:139905123249544
  内部:23185912
  外部:139905123249544
  外部:23185912
  内部:139905142421000
  内部:23180456
  内部:139905123267144
  内部:23182184
  内部:139905123249544
  内部:23183912
  内部:139905123249800
  内部:23185640

SYNC = False

  

开始测试异步
  内部:139699492382216
  内部:38987640
  内部:139699490987656
  内部:38989368
  内部:139699490985992
  内部:38991096
  内部:139699490986120
  内部:38992824
  外部:139699490985992
  外部:139699180021064
  外部:139699490986120
  外部:139699180022888
  外部:139699473207560
  外部:139699180024712
  外部:139699473207880
  外部:139699180026536

1 个答案:

答案 0 :(得分:1)

我认为您不了解多处理的工作原理。 multiprocessing启动新的python进程以运行您的代码。每个进程都有自己的内存空间。当您将inputs传递给映射时,每个进程都会在其自己的内存空间中获取数据的副本。请参阅有关此问题的答案:Python multiprocessing and a shared counter

如果您确实想要数据的单个副本,则应使用Shared Memory。 警告:使用起来很麻烦。

https://docs.python.org/dev/library/multiprocessing.shared_memory.html

这是文档中的一个示例:

>>> with SharedMemoryManager() as smm:
...     sl = smm.ShareableList(range(2000))
...     # Divide the work among two processes, storing partial results in sl
...     p1 = Process(target=do_work, args=(sl, 0, 1000))
...     p2 = Process(target=do_work, args=(sl, 1000, 2000))
...     p1.start()
...     p2.start()  # A multiprocessing.Pool might be more efficient
...     p1.join()
...     p2.join()   # Wait for all work to complete in both processes
...     total_result = sum(sl)  # Consolidate the partial results now in sl