2

如何在 Python 进程之间共享嵌套对象,并对 tasklet(协程)进行写访问?

这是一个简化的例子,我只是为了正确地提出这个问题而写的一个类比;

首先请安装greenlet包:sudo pip install greenlet

在下面的示例中:

  • 变量Nature引用的类的实例habitat
  • 这个Nature类的实例有一个名为的实例变量animals
  • 在启动这个Nature类实例的同时,创建了 8 个不同的Animal类实例并将其附加到animals实例变量中。现在,如果我是正确的,这个实例Nature是一个嵌套对象。
  • 作为实例的最后一步live实例函数Animal使用greenlet包的switch()函数随机切换,直到global_counter达到1000。该live函数随机改变实例的limbs实例变量的值Animal

绿色测试.py

import random
from greenlet import greenlet

global_counter = 0

class Animal():

    def __init__(self,nature):
        self.limbs = 0
        nature.animals.append(self)
        self.tasklet = greenlet(self.live)

    def live(self,nature):
        global global_counter
        while True:
            self.limbs = random.randint(1, 10)
            global_counter += 1
            if global_counter > 1000:
                break
            random.sample(nature.animals,1)[0].tasklet.switch(nature)

class Nature():

    def __init__(self,how_many):
        self.animals = []
        for i in range(how_many):
            Animal(self)
        print str(how_many) + " animals created."
        self.animals[0].live(self)

结果是:

>>> import greentest
>>> habitat = greentest.Nature(8)
8 animals created.
>>> habitat.animals[0].limbs
3
>>> greentest.global_counter
1002

按预期工作。改变limbsand的值global_counter(非零)

但是当我加入multiprocessing等式时;

greentest2.py

import random
import multiprocessing
from greenlet import greenlet

global_counter = 0

class Animal():

    def __init__(self,nature):
        self.limbs = 0
        nature.animals.append(self)
        self.tasklet = greenlet(self.live)

    def live(self,nature):
        global global_counter
        while True:
            self.limbs = random.randint(1, 10)
            global_counter += 1
            if global_counter > 1000:
                break
            random.sample(nature.animals,1)[0].tasklet.switch(nature)

class Nature():

    def __init__(self,how_many):
        self.animals = []
        for i in range(how_many):
            Animal(self)
        print str(how_many) + " animals created."
        #self.animals[0].live(self)
        jobs = []
        for i in range(2):
            p = multiprocessing.Process(target=self.animals[0].live, args=(self,))
            jobs.append(p)
            p.start()

结果并不如预期:

>>> import greentest2
>>> habitat = greentest2.Nature(8)
8 animals created.
>>> habitat.animals[0].limbs
0
>>> greentest2.global_counter
0

limbs和的值global_counter都不变(零)。我认为这是因为Animal类的实例并global_counter没有在进程之间共享。那么如何在进程之间共享这个Nature类的实例或这些类的实例呢?Animal

根据@noxdafox 的回答添加

greentest3.py

import random
import multiprocessing
from greenlet import greenlet

global_counter = multiprocessing.Value('i', 0)

class Animal():

    def __init__(self,nature):
        self.limbs = 0
        nature.animals.append(self)
        self.tasklet = greenlet(self.live)

    def live(self,nature):
        global global_counter
        while True:
            self.limbs = random.randint(1, 10)
            global_counter.value += 1
            if global_counter.value > 1000:
                break
            random.sample(nature.animals,1)[0].tasklet.switch(nature)

class Nature():

    def __init__(self,how_many):
        self.animals = []
        for i in range(how_many):
            Animal(self)
        print str(how_many) + " animals created."
        #self.animals[0].live(self)
        jobs = []
        for i in range(2):
            p = multiprocessing.Process(target=self.animals[0].live, args=(self,))
            jobs.append(p)
            p.start()

然后结果是:

>>> import greentest3
>>> habitat = greentest3.Nature(8)
8 animals created.
>>> habitat.animals[0].limbs
0
>>> greentest3.global_counter.value
1004

我完全知道global_counter可以与此方法共享,因为它是一个整数,但我实际上是在询问如何在进程之间共享实例Nature和类。Animal

4

1 回答 1

1

不同的进程不共享它们的内存。

如果您需要共享的是单个变量,您可能可以使用multiprocessing.Value

import multiprocessing

def function(counter):
    counter.value += 1

counter = multiprocessing.Value('i')
p = multiprocessing.Process(target=function, args=(counter))
p.start()
p.join()

编辑:根据更新回答。

没有抽象机制允许共享内存中的整个对象。共享内存通常被实现为一个简单的数组,一旦获得资源,进程就可以读/写。

此外,OOP 和线程/多处理不能很好地混合在一起。恕我直言,应将其视为反模式。在复杂对象之上,您添加了对其属性的并发访问和修改。这是冗长乏味的调试会话的单程票。

推荐的模式是使用消息队列。将线程和进程想象为通过特定通道进行通信的孤立实体可以显着简化问题。

于 2016-12-12T20:39:22.727 回答