2016-11-29 18 views
1

多くのプロセスから、ピック可能な大型のPythonオブジェクトのセットにアクセスする必要があります。したがって、これらのオブジェクトが完全にコピーされないようにしたいと思います。LinuxシステムでのPythonマルチプロセッシングでフォークの動作を確認する

thisthisのコメントによると、オブジェクトは変更されない限り、(UNIXシステムでは)コピーされません。ただし、オブジェクトを参照すると参照カウントが変更され、参照カウントがコピーされます。

これまでのところ正しいですか?私の関心は私の大きなオブジェクトのサイズに起因するので、これらのオブジェクトの小さな部分がコピーされても問題はありません。 fの最初の行で

from multiprocessing import Pool 

def f(arg): 
    print(l, id(l), object.__repr__(l)) 
    l[arg] = -1 
    print(l, id(l), object.__repr__(l)) 

def test(n): 
    global l 
    l = list(range(n)) 
    with Pool() as pool: 
     pool.map(f, range(n)) 
    print(l, id(l), object.__repr__(l)) 

if __name__ == '__main__': 
    test(5) 

、私はすべての機能に同じ番号を返すようにid(l)を期待:

は、私はすべてを正しく理解し、予想外の何も起こらないことを、私は小さなテストプログラムを実施していることを確認するために、電話番号は idチェックの前に変更されていないためです。

一方、fの3行目では、リストが2行目で変更されるため、id(l)は各メソッド呼び出しで異なる番号を返す必要があります。

しかし、プログラムの出力が私を困惑させる。

[0, 1, 2, 3, 4] 139778408436488 <list object at 0x7f20b261d308> 
[-1, 1, 2, 3, 4] 139778408436488 <list object at 0x7f20b261d308> 
[0, 1, 2, 3, 4] 139778408436488 <list object at 0x7f20b261d308> 
[0, -1, 2, 3, 4] 139778408436488 <list object at 0x7f20b261d308> 
[0, 1, 2, 3, 4] 139778408436488 <list object at 0x7f20b261d308> 
[0, 1, -1, 3, 4] 139778408436488 <list object at 0x7f20b261d308> 
[0, 1, 2, 3, 4] 139778408436488 <list object at 0x7f20b261d308> 
[0, 1, 2, -1, 4] 139778408436488 <list object at 0x7f20b261d308> 
[0, 1, 2, 3, 4] 139778408436488 <list object at 0x7f20b261d308> 
[0, 1, 2, 3, -1] 139778408436488 <list object at 0x7f20b261d308> 
[0, 1, 2, 3, 4] 139778408436488 

idはすべてfのすべてのコールと回線で同じです。これは、リストが最後に変更されていなくても(期待通り)、リストにはがコピーされていることを意味します。

オブジェクトがコピーされているかどうかを確認する方法はありますか?

+0

オブジェクトが作成されると、そのオブジェクトのIDは変更できません。 –

+0

@RossRidge:オブジェクトをコピーするとき、元のものとは異なる場所にあるはずですか?このメモリアドレスを取得する方法はありますか( 'id'が何かを返すかどうか)? – Samufi

+0

ありがとう、@ジョナサンユーニス。私は声明を訂正しようとしました。それは今大丈夫ですか? – Samufi

答えて

2

あなたの混乱は、プロセスの理解を誤解していると思われ、forkが機能しているようです。各プロセスには独自のアドレス空間があり、2つのプロセスが同じアドレスを矛盾なく使用できます。これは、同じメモリが両方のプロセスにマッピングされていない限り、プロセスが別のプロセスのメモリにアクセスできないことを意味します。

プロセスがforkシステムコールを呼び出すと、オペレーティングシステムは親プロセスのクローンである新しい子プロセスを作成します。このクローンは、他のプロセスと同様、親とは異なる独自のアドレス空間を持っています。しかし、アドレス空間の内容は親の正確なコピーです。これは、親プロセスのメモリをその子に割り当てられた新しいメモリにコピーすることによって達成されていました。これは、いずれかのプロセスが自分のメモリに行う変更が他方に影響を与えない限り、子と親がforkの後に実行を再開すると、これを意味します。

しかし、プロセスのアドレス空間全体をコピーするのは高価な操作であり、通常は無駄です。ほとんどの場合、新しいプロセスはすぐに新しいプログラムを実行し、その結果、子のアドレス空間が完全に置き換えられます。だから最近のUnixライクなオペレーティングシステムでは、 "コピーオンライト" forkの実装を使用しています。親プロセスのメモリをコピーする代わりに、親のメモリは、同じメモリを共有できるように、その子にマッピングされます。しかし、古いセマンティクスは依然として維持されています。子または親のいずれかが共有メモリを変更すると、変更されたページがコピーされ、2つのプロセスがそのメモリページを共有しなくなります。

multiprocessingモジュールがf関数を呼び出すと、forkシステムコールを使用して作成された子プロセスでその機能が呼び出されます。この子プロセスは親プロセスのクローンであるため、同じプロセスIDで同じID(アドレス)と内容を持つリストを参照するグローバル変数lもあります。つまり、子プロセスでlが参照するリストを変更するまでです。 IDは変化しませんが、子のバージョンは親のものと同じではありません。親のリストの内容は、子によって行われた変更の影響を受けません。

forkがcopy-on-writeを使用するかどうかにかかわらず、前の段落で説明した動作が真であることに注意してください。限り、multiprocessingモジュールとPythonの一般的な懸念事項は実装の詳細です。有効な結果は関係なく同じです。つまり、forkの実装が使用されているPythonプログラムでは、実際にテストすることはできません。

関連する問題