我有一个在大图结构上运行的算法,我想制作多线程以获得更好的性能。我所看到的所有方法都不符合我的要求:我希望图形存在于共享内存中,所有进程都可以读取和写入(使用锁来防止竞争条件)。从本质上讲,我想在C中使用类似OpenMP的东西,每个线程都可以访问所有内存。
我从查看线程模块开始,但GIL意味着性能提升无关紧要。
我继续尝试多处理模块,正如我在此主题上发现的大多数帖子(例如how can I share a dictionary across multiple processes?和Shared-memory objects in python multiprocessing)所建议的那样。这有两个主要问题。
首先,似乎多处理对复杂对象不起作用。考虑以下玩具问题:我有一个整数列表,并希望将它们全部乘以10,然后以任意顺序输出所有数字。我可以使用以下代码:
def multiply_list():
manager = Manager()
output = manager.list()
threads = []
for v in range(10):
output.append(v)
print([str(v) for v in output])
def process(inputs, start, end):
while start < end:
inputs[start] *= 10
start += 1
t1 = Process(target=process,
args = (output, 0, 5))
t2 = Process(target=process,
args = (output, 5, 10))
t1.start()
t2.start()
t1.join()
t2.join()
print([str(v) for v in output])
带输出:
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
['0', '10', '20', '30', '40', '50', '60', '70', '80', '90']
但是,如果我有一个对象列表,并修改对象:
class Container(object):
def __init__(self, value):
self.value = value
def __str__(self):
return "C" + str(self.value)
def multiply_containers():
manager = Manager()
output = manager.list()
threads = []
for v in range(10):
output.append(Container(v))
print([str(v) for v in output])
def process(inputs, start, end):
while start < end:
inputs[start].value *= 10
start += 1
t1 = Process(target=process,
args = (output, 0, 5))
t2 = Process(target=process,
args = (output, 5, 10))
t1.start()
t2.start()
t1.join()
t2.join()
print([str(v) for v in output])
没有变化。
['C0', 'C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7', 'C8', 'C9']
['C0', 'C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7', 'C8', 'C9']
另一个问题是我链接的SO帖子建议尝试写入数据结构会复制它,这是我不想要的。
为了澄清算法本身,第一步(构建图形)的工作原理如下:我有一个句子列表,它是单词序列。我想构建一个有向图,其中每个顶点都是一个单词,其外边缘会在某个句子中跟随每个单词。例如,如果我的输入是“帽子里的猫”和“房子里的猫”,我的输出图将是=&gt; cat =&gt; in =&gt; =&gt;帽子,房子(即“the”有两个外边,一个是“帽子”,一个是“房子”)。我还会跟踪一些辅助信息,例如每个句子或单词的常见程度。每个顶点都有一个入边和出边列表以及一些属性。
我发现了一个可能有效的模块(http://poshmodule.sourceforge.net/posh/html/),但我不确定是否有“规范”或推荐的方式来做这类事情。
谢谢!
答案 0 :(得分:1)
下面是示例代码(可行),它使用单独的Manager进程来控制对共享数据结构的访问,并且基于您的示例代码以及问题Sharing object (class instance) in python using Managers中的问题,@ freakish说这可能是一个重复的问题在评论中 - 我不清楚它是否存在,但整体方法似乎可以解决您的问题。
from multiprocessing import Lock, Manager, Process
from multiprocessing.managers import BaseManager
class Container(object):
def __init__(self, value):
self.value = value
def __str__(self):
return "C" + str(self.value)
def multiply(self, factor): # added method
self.value *= factor
def process(inputs, start, end):
for i in range(start, end):
inputs.apply(i, 'multiply', (10,))
class ListProxy(object):
def __init__(self):
self.nl = []
def append(self, x):
self.nl.append(x)
def __getitem__(self, key):
return self.nl[key]
def __iter__(self):
return iter(self.nl)
def apply(self, i, method, args, **kwargs):
getattr(self.nl[i], method)(*args, **kwargs)
class ListManager(BaseManager):
pass
ListManager.register('ListProxy', ListProxy,
exposed=['append', '__getitem__', '__iter__', 'apply'])
def main():
manager = ListManager()
manager.start()
output = manager.ListProxy()
for v in range(10):
output.append(Container(v))
print([str(v) for v in output])
t1 = Process(target=process, args=(output, 0, 5))
t2 = Process(target=process, args=(output, 5, 10))
t1.start()
t2.start()
t1.join()
t2.join()
print([str(v) for v in output])
if __name__ == '__main__':
main()