3

私は Experiment と呼ばれるクラスと Case と呼ばれる別のクラスを持っています。1 つの実験は、多くの個別のケースで構成されています。以下のクラス定義を参照してください。

from multiprocessing import Process

class Experiment (object):
    def __init__(self, name):
        self.name = name
        self.cases = []
        self.cases.append(Case('a'))
        self.cases.append(Case('b'))
        self.cases.append(Case('c'))

    def sr_execute(self):
        for c in self.cases:  
            c.setVars(6)

class Case(object):
    def __init__(self, name):
        self.name = name
    def setVars(self, var):
        self.var = var

私の実験クラスには、sr_execute という関数があります。この関数は、望ましい動作を示します。すべてのケースを解析して、ケースごとに属性を設定することに興味があります。次のコードを実行すると、

if __name__ == '__main__':
    #multiprocessing.freeze_support()
    e = Experiment('exp')
    e.sr_execute()
    for c in e.cases: print c.name, c.var

私は、

a 6
b 6
c 6

これは望ましい動作です。

ただし、マルチプロセッシングを使用してこれを並行して行いたいと考えています。これを行うには、実験クラスに mp_execute() 関数を追加します。

    def mp_execute(self):
        processes = []
        for c in self.cases: 
            processes.append(Process(target= c.setVars, args = (6,)))
        [p.start() for p in processes]
        [p.join() for p in processes]

ただし、これは機能しません。以下を実行すると、

if __name__ == '__main__':
    #multiprocessing.freeze_support()
    e = Experiment('exp')
    e.mp_execute()
    for c in e.cases: print c.name, c.var

エラーが発生します。

AttributeError: 'Case' object has no attribute 'var'

どうやら、マルチプロセッシングを使用してクラス属性を設定できません。

何が起こっているかの手がかり、

4

2 に答える 2

3

電話すると:

def mp_execute(self):
    processes = []
    for c in self.cases: 
        processes.append(Process(target= c.setVars, args = (6,)))
    [p.start() for p in processes]
    [p.join() for p in processes]

を作成すると、オブジェクトのコピーProcessが使用され、プロセスごとにアドレス空間が異なるため、そのようなオブジェクトへの変更はメインプログラムに渡されません。sを使用すると機能します。その場合、コピーは作成されません。Thread

targetまた、メソッドを as として渡しており、Windows ではtargetが pickle 可能である必要がある (インスタンス メソッドはpickable ではない)ため、Windows ではおそらくコードが失敗することに注意してください。すべてのtargetOses で機能するためには、モジュールのトップ レベルで定義された関数である必要があります。

メインプロセスに変更を伝えたい場合は、次のことができます。

  • a を使用しQueueて結果を渡します
  • a を使用しManagerて共有オブジェクトを構築する

とにかく、「チャネル」(のような)を設定するか、共有状態を設定することにより、通信を「明示的に」処理する必要があります。Queue


スタイル ノート:リスト内包表記を次のように使用しないでください。

[p.join() for p in processes]

それは単に間違っています。のリストを作成してスペースを浪費しているだけですNone。また、正しい方法に比べておそらく遅くなります。

for p in processes:
    p.join()

リストに要素を追加する必要があるためです。

forただし、リスト内包表記はループよりもわずかに高速であると言う人もいます。

  • パフォーマンスの違いは非常に小さいため、一般的には問題になりません
  • この種のループを考慮した場合にのみ、高速になります。

    a = []
    for element in something:
        a.append(element)
    

    この場合のように、ループが を作成しないlist場合、forループは高速になります。

ちなみに、map副作用を実行するために同じ方法で使用するものもあります。以前と同じ理由で速度が大幅に向上せず、イテレータを返すpython3 で完全に失敗し、関数がまったく実行されないため、コードの移植性が低下するため、これも間違っています。map

于 2013-11-07T07:48:14.433 に答える
2

@Bakuriu の回答は、優れたスタイリングと効率の提案を提供します。また、各プロセスはマスター プロセス スタックのコピーを取得するため、フォークされたプロセスによって行われた変更は、何らかの形式の IPC (キュー、パイプ、マネージャーなど) を利用しない限り、マスター プロセスのアドレス空間に反映されません。

ただし、発生している特定のAttributeError: 'Case' object has no attribute 'var'エラーには別の理由があります。つまり、プロセスを起動した時点でCase オブジェクトに属性がまだないためです。var代わりにvar、メソッドで属性が作成されますsetVars()

フォークされたプロセスは、呼び出し時に実際に変数を作成しますsetVars()(実際には変数を 6 に設定します) が、残念ながら、この変更は Case オブジェクトのコピーにのみ反映されます。つまり、マスター プロセスのメモリ空間には反映されません (変数がまだ行われている場所)。存在しない)。

私の言いたいことを理解するには、Case クラスを次のように変更します。

class Case(object):
    def __init__(self, name):
        self.name = name
        self.var = 7  # Create var in the constructor.
    def setVars(self, var):
        self.var = var

コンストラクターにメンバー変数を追加することによりvar、マスター プロセスはそれにアクセスできるようになります。もちろん、フォークされたプロセスの変更はマスター プロセスには反映されませんが、少なくともエラーは発生しません。

a 7
b 7
c 7

これが何が起こっているのかを明らかにすることを願っています。=)


解決:

(元のコードに対して) 最も侵入的でないことは、共有メモリから ctypes オブジェクトを使用することです。

from multiprocessing import Value
class Case(object):
    def __init__(self, name):
        self.name = name
        self.var = Value('i', 7)              # Use ctypes "int" from shared memory.
    def setVars(self, var):
        self.var.value = var                  # Set the variable's "value" attribute.

main() を変更して c.var.value を出力します。

for c in e.cases: print c.name, c.var.value   # Print the "value" attribute.

これで、目的の出力が得られました。

a 6
b 6
c 6
于 2013-11-08T16:17:07.707 に答える