この問題に関する私のメモをここに書き留めてもよろしいかと思います。
まず第一に、私はOPの例にとても感謝しています。なぜなら、それは私が始めた場所でもあるからです. [Tutor]モジュール間のグローバル変数shared
で完全な例を見つけるまで、組み込みのPythonモジュールだと思いましたが?? .
ただし、「スクリプト間で変数を共有する」(またはプロセス)を探したとき-Pythonスクリプトが他のPythonソースファイルで定義された変数を使用する必要がある場合(ただし、必ずしも実行中のプロセスであるとは限らない)の場合を除いて-私は主に2つの他のユースケースに出くわしました:
- スクリプトは複数の子プロセスに分岐し、同じ PC 上で並行して (場合によっては複数のプロセッサで) 実行されます。
- スクリプトは他の複数の子プロセスを生成し、それらは同じ PC 上で (おそらく複数のプロセッサで) 並列に実行されます。
そのため、「共有変数」と「プロセス間通信」(IPC) に関するほとんどのヒットは、これら 2 つのようなケースについて議論しています。ただし、どちらの場合でも、通常は「子」が参照する「親」を観察できます。
ただし、私が興味を持っているのは、同じスクリプトの複数の呼び出しを実行し、独立して実行し、それらの間でデータを共有することです ( Python のように: スクリプトの複数の呼び出しでオブジェクト インスタンスを共有する方法)、シングルトン/単一インスタンスでモード。この種の問題は、上記の 2 つのケースでは実際には対処されません。代わりに、本質的に OP の例 (2 つのスクリプト間で変数を共有する) に縮小されます。
さて、Perl でこの問題を扱う場合、IPC::Shareableがあります。これにより、「プロセス空間全体のデータの共通識別子として機能する整数または4文字の文字列[1]」を使用して、「変数を共有メモリに関連付けることができます」。したがって、一時ファイルもネットワーク設定もありません。これは私のユースケースに最適です。だから私はPythonで同じものを探していました。
ただし、@Drewfer による回答が受け入れられているように、「インタープリターの 2 つのインスタンスの外部に情報を保存しなければ、やりたいことを実行することはできません」。つまり、ネットワーク/ソケットのセットアップを使用する必要があります-または一時ファイルを使用する必要があります(したがって、「完全に個別のpythonセッション」用の共有RAMはありません)。
さて、これらの考慮事項があっても、実際の例を見つけるのはちょっと難しいです (を除く) - mmapおよびmultiprocessingpickle
のドキュメントでも。ドキュメントに記載されていないいくつかの落とし穴についても説明している他の例をいくつか見つけることができました。
- の使用法: mmap を使用してプロセス間で Python データを共有
mmap
する の 2 つの異なるスクリプトでの作業コード| シュマイケルのブログ
- 両方のスクリプトが共有値を変更する方法を示します
- ここでは、保存されたデータのストレージとして一時ファイルが作成されることに注意してください。
mmap
これは、この一時ファイルにアクセスするための特別なインターフェイスです。
- の使用法
multiprocessing
: 作業コード:
これらの例のおかげで、「 python dict を同期するmmap
」例からのアプローチで、基本的に例と同じことを行う例を思いつきました- (ファイルパスアドレスを介して)共有リストを使用します。サーバーとクライアントの両方が読み取りと書き込みを行います (以下に貼り付けます)。ご了承ください:BaseManager
manager.start()
multiprocessing
manager.start()
manager は、またはのいずれかで開始できます。server.serve_forever()
serve_forever()
ロック -start()
しません
- には自動ロギング機能があります
multiprocessing
: start()
ed プロセスでは問題なく動作するようですが、そのようなプロセスは無視しているようです。serve_forever()
- のアドレス指定は、
multiprocessing
IP (ソケット) または一時ファイル (おそらくパイプ?) パスです。multiprocessing
ドキュメント
で:
- ほとんどの例で使用- これは、 ;の特別なサブクラスであるを返す
multiprocessing.Manager()
単なる関数 (クラスのインスタンス化ではない) です。and uses - ただし、独立して実行されたスクリプト間の IPC には使用されません。ここではファイルパスが使用されますSyncManager
BaseManager
start()
serve_forever()
独立して実行されたスクリプト間の IPC のアプローチの例は他にほとんどありません。ここではIP /ソケットアドレスが使用されています
- アドレスが指定されていない場合、一時ファイルのパスが自動的に使用されます (これを確認する方法の例については、16.6.2.12. ログを参照してください)。
「 python dict を同期する」投稿のすべての落とし穴に加えて、リストの場合には追加の落とし穴があります。その投稿は次のように述べています。
dict のすべての操作は、dict 割り当てではなくメソッドで行う必要があります (マルチプロセッシングがカスタム オブジェクトを共有する方法のため、syncdict["blast"] = 2 は惨めに失敗します)。
dict['key']
取得と設定の回避策は、dict
パブリック メソッドget
と を使用することですupdate
。list[index]
問題は、 ;の代替となるパブリック メソッドがないことです。したがって、共有リストの場合、さらに__getitem__
と__setitem__
メソッド ( のプライベートlist
) を としてexposed
登録する必要があります。つまり、 のすべてのパブリック メソッドも再登録する必要list
があります。:/
ええと、それらが最も重要なことだったと思います。これらは 2 つのスクリプトです。これらは別々のターミナルで実行できます (サーバーが最初)。Python 2.7 を使用して Linux で開発されたノート:
a.py
(サーバ):
import multiprocessing
import multiprocessing.managers
import logging
logger = multiprocessing.log_to_stderr()
logger.setLevel(logging.INFO)
class MyListManager(multiprocessing.managers.BaseManager):
pass
syncarr = []
def get_arr():
return syncarr
def main():
# print dir([]) # cannot do `exposed = dir([])`!! manually:
MyListManager.register("syncarr", get_arr, exposed=['__getitem__', '__setitem__', '__str__', 'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'])
manager = MyListManager(address=('/tmp/mypipe'), authkey='')
manager.start()
# we don't use the same name as `syncarr` here (although we could);
# just to see that `syncarr_tmp` is actually <AutoProxy[syncarr] object>
# so we also have to expose `__str__` method in order to print its list values!
syncarr_tmp = manager.syncarr()
print("syncarr (master):", syncarr, "syncarr_tmp:", syncarr_tmp)
print("syncarr initial:", syncarr_tmp.__str__())
syncarr_tmp.append(140)
syncarr_tmp.append("hello")
print("syncarr set:", str(syncarr_tmp))
raw_input('Now run b.py and press ENTER')
print
print 'Changing [0]'
syncarr_tmp.__setitem__(0, 250)
print 'Changing [1]'
syncarr_tmp.__setitem__(1, "foo")
new_i = raw_input('Enter a new int value for [0]: ')
syncarr_tmp.__setitem__(0, int(new_i))
raw_input("Press any key (NOT Ctrl-C!) to kill server (but kill client first)".center(50, "-"))
manager.shutdown()
if __name__ == '__main__':
main()
b.py
(クライアント)
import time
import multiprocessing
import multiprocessing.managers
import logging
logger = multiprocessing.log_to_stderr()
logger.setLevel(logging.INFO)
class MyListManager(multiprocessing.managers.BaseManager):
pass
MyListManager.register("syncarr")
def main():
manager = MyListManager(address=('/tmp/mypipe'), authkey='')
manager.connect()
syncarr = manager.syncarr()
print "arr = %s" % (dir(syncarr))
# note here we need not bother with __str__
# syncarr can be printed as a list without a problem:
print "List at start:", syncarr
print "Changing from client"
syncarr.append(30)
print "List now:", syncarr
o0 = None
o1 = None
while 1:
new_0 = syncarr.__getitem__(0) # syncarr[0]
new_1 = syncarr.__getitem__(1) # syncarr[1]
if o0 != new_0 or o1 != new_1:
print 'o0: %s => %s' % (str(o0), str(new_0))
print 'o1: %s => %s' % (str(o1), str(new_1))
print "List is:", syncarr
print 'Press Ctrl-C to exit'
o0 = new_0
o1 = new_1
time.sleep(1)
if __name__ == '__main__':
main()
最後の発言として、Linux では/tmp/mypipe
作成されますが、0 バイトであり、srwxr-xr-x
(ソケットの) 属性があります。ネットワークポートや一時ファイル自体について心配する必要がないので、これは私を幸せにすると思います:)
その他の関連する質問: