2

タスクレット (コルーチン) への書き込みアクセス権を持つ Python プロセス間でネストされたオブジェクトを共有するにはどうすればよいですか?

これは、この質問を適切に行うために私が書いたアナロジーを使用した単純化された例です。

まず、次のgreenletパッケージをインストールしてください。sudo pip install greenlet

以下の例では:

  • 変数Natureによって参照されるクラスのインスタンスhabitat
  • クラスのこのインスタンスにNatureは、というインスタンス変数がありますanimals
  • クラスのこのインスタンスの開始中に、Natureクラスの 8 つの異なるインスタンスが作成され、インスタンス変数Animalに追加されました。animals私が正しければ、このインスタンスNatureはネストされたオブジェクトです。
  • 最後のステップとして、liveインスタンスのインスタンス関数は、パッケージの関数Animalを使用して 1000 に達するまでランダムに切り替えます。この関数は、インスタンスのインスタンス変数の値をランダムに変更します。greenletswitch()global_counterlivelimbsAnimal

greentest.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であるため、このメソッドと共有できることは完全に認識していましたが、実際には、プロセス間でインスタンスとクラスを共有する方法を尋ねています。NatureAnimal

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 に答える