我的问题很简单,我需要将函数映射到值列表,并返回最大值。因此,如果您喜欢这个术语,那么可以在映射之后进行缩减,即映射缩减。
作为 python 的新手,我读到,我应该使用多处理而不是线程,这样就可以了,但我不知道这是否会削弱我的程序。问题是我需要映射的函数需要多个参数,并且它需要的一些数据结构非常庞大。所以,我担心随着大数据被传递到进程,结果会创建新的副本,这可能是 Not Acceptable 。
对于解决这个简单的问题,您有什么建议?我是否会面临多个副本,我应该手动创建一些共享内存,还是虚拟机会自动为我创建它?
谢谢!
最佳答案
您有多种选择来实现此目的,每种选择都有各自的优点和缺点。
参数列表
让我们先解决一个简单的问题:传递数据结构肯定会为每个进程创建一个副本。听起来这不是你想要的。
管理者和代理
我建议您先尝试一下这个方法。多处理库支持proxy objects 。它们的作用类似于共享对象的别名,并且如果您正在处理列表和字典等 native 类型,则很容易使用。此方法甚至允许您安全地修改共享对象,而不必担心锁定细节,因为管理器会处理它们。既然您提到了列表,这可能是您最好的选择。您还可以创建您自己的自定义对象的代理。
全局数据结构
在某些情况下,可接受的解决方案是将数据结构设为全局数据结构,作为将它们作为参数传递的替代方案。如果您只从进程中读取它们,则不会在进程之间复制它们。当人们没有意识到创建对全局变量的本地引用也算作对其进行写入时,这可能会让人们陷入困境,因为该变量的 reference count必须递增。这就是垃圾收集器知道何时可以释放内存的方式:如果对象的引用计数为零,则表明没有人在使用它,并且可以将其删除。下面的代码片段演示了这一点:
import sys
import multiprocessing
global_var = [1, 2, 3]
def no_effect1():
print(global_var[0] + global_var[1])
print("No Effect Count: {}".format(sys.getrefcount(global_var)))
return
def no_effect2():
new_list = [i for i in global_var]
print("New List Count: {}".format(sys.getrefcount(global_var)))
def change1():
local_handle = global_var
print("Local Handle Count: {}".format(sys.getrefcount(global_var)))
def change2():
new_dict = {'item':global_var}
print("Contained Count: {}".format(sys.getrefcount(global_var)))
p_list = [multiprocessing.Process(target=no_effect1),
multiprocessing.Process(target=no_effect2),
multiprocessing.Process(target=change1),
multiprocessing.Process(target=change2)]
for p in p_list:
p.start()
for p in p_list:
p.join()
p.join()
此代码产生以下输出:
3
No Effect Count: 2
New List Count: 2
Local Handle Count: 3
Contained Count: 3
在no_effect1()
函数中,我们能够在不增加引用计数的情况下读取和使用全局结构中的数据。 no_effect2()
从全局结构构造一个新列表。在这两种情况下,我们都会读取全局变量,但不会创建对同一底层内存的任何本地引用。如果您以这种方式使用全局数据结构,那么您将不会导致它们在进程之间复制。
但是,请注意,在 change1()
和 change2()
中,引用计数会增加,因为我们将局部变量绑定(bind)到相同的数据结构。这意味着我们已经修改了全局结构并且它将被复制。
共享 Ctypes
如果您可以将共享数据巧妙地放入 C 数组中,则可以使用 shared Ctypes 。这些是从共享内存分配的数组(或单个值)。然后,您可以传递包装器,而不复制底层数据。
MMAP
您还可以创建一个共享内存映射来放入数据,但它可能会变得复杂,并且我仅建议在代理和全局选项不适合您时这样做。有一个blog post here有一个很好的例子。
其他想法
有一点挑剔:在您的问题中,您提到了“VM”。由于您没有指定您在虚拟机上运行,因此我假设您将 Python 解释器称为虚拟机。请记住,Python 是一个解释器,并且不提供像 Java 那样的虚拟机环境。界限确实很模糊,并且术语的正确使用也存在争议,但人们通常不会将 Python 解释器称为虚拟机。查看this SO question的优秀答案有关差异的更细致的解释。
关于Python 映射函数、线程、大结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26489884/