pythonマルチプロセッシングプールの各ワーカーに一意のIDを割り当てる方法はあります。プール内の特定のワーカーによって実行されているジョブが、どのワーカーを実行しているかを知ることができますか?ドキュメント、Process
にはname
がありますが、
名前は、識別目的でのみ使用される文字列です。セマンティクスはありません。複数のプロセスに同じ名前を付けることができます。
特定のユースケースでは、4つのGPUのグループで多数のジョブを実行し、ジョブを実行するGPUのデバイス番号を設定する必要があります。ジョブの長さは不均一であるため、前のジョブが完了する前に実行しようとしているジョブのGPUで衝突が発生しないようにしたい(したがって、IDを事前に割り当てることはできません事前に作業単位)。
multiprocessing.current_process()
という単純なもののようです。例えば:
_import multiprocessing
def f(x):
print multiprocessing.current_process()
return x * x
p = multiprocessing.Pool()
print p.map(f, range(6))
_
出力:
_$ python foo.py
<Process(PoolWorker-1, started daemon)>
<Process(PoolWorker-2, started daemon)>
<Process(PoolWorker-3, started daemon)>
<Process(PoolWorker-1, started daemon)>
<Process(PoolWorker-2, started daemon)>
<Process(PoolWorker-4, started daemon)>
[0, 1, 4, 9, 16, 25]
_
これにより、プロセスオブジェクト自体が返されるため、プロセスは独自のIDになります。一意の数値IDに対してid
を呼び出すこともできます-cpythonでは、これはプロセスオブジェクトのメモリアドレスなので、私はしませんthink重複する可能性があります。最後に、プロセスのident
またはpid
プロパティを使用できますが、これはプロセスが開始された後にのみ設定されます。
さらに、ソースを見ると、自動生成された名前(上記のProcess
repr文字列の最初の値で例示されている)は一意である可能性が非常に高いようです。 multiprocessing
は、プロセスごとに_itertools.counter
_オブジェクトを保持します。これは、生成されるすべての子プロセスの __identity
_ タプルを生成するために使用されます。したがって、最上位プロセスは、単一値IDを持つ子プロセスを生成し、2値IDを持つプロセスを生成します。次に、Process
コンストラクターに名前が渡されない場合は、':'.join(...)
を使用して、_identityに基づいて単純に 名前を自動生成 します。次に、Pool
名前を変更 を使用してプロセスのreplace
を使用し、自動生成されたIDは同じままにします。
これらすべての結果は、2つのProcess
es mayは同じ名前ですが、それらを作成するときにmayに同じ名前を割り当てるためです。 nameパラメーターに触れない場合は一意です。また、理論的には一意の識別子として__identity
_を使用できます。しかし、私は彼らが理由でその変数をプライベートにしたと集めています!
動作中の上記の例:
_import multiprocessing
def f(x):
created = multiprocessing.Process()
current = multiprocessing.current_process()
print 'running:', current.name, current._identity
print 'created:', created.name, created._identity
return x * x
p = multiprocessing.Pool()
print p.map(f, range(6))
_
出力:
_$ python foo.py
running: PoolWorker-1 (1,)
created: Process-1:1 (1, 1)
running: PoolWorker-2 (2,)
created: Process-2:1 (2, 1)
running: PoolWorker-3 (3,)
created: Process-3:1 (3, 1)
running: PoolWorker-1 (1,)
created: Process-1:2 (1, 2)
running: PoolWorker-2 (2,)
created: Process-2:2 (2, 2)
running: PoolWorker-4 (4,)
created: Process-4:1 (4, 1)
[0, 1, 4, 9, 16, 25]
_
_multiprocessing.Queue
_を使用してIDを保存し、プールプロセスの初期化時にIDを取得できます。
利点:
queue.get()
でブロックし、作業を実行しません(これはポーグラムをブロックしませんが、または少なくとも私がテストしたときはそうではなかった)。短所:
sleep(1)
がなければ、他のプロセスはまだ初期化されていないため、最初のプロセスですべての作業が実行されます。例:
_import multiprocessing
from time import sleep
def init(queue):
global idx
idx = queue.get()
def f(x):
global idx
process = multiprocessing.current_process()
sleep(1)
return (idx, process.pid, x * x)
ids = [0, 1, 2, 3]
manager = multiprocessing.Manager()
idQueue = manager.Queue()
for i in ids:
idQueue.put(i)
p = multiprocessing.Pool(8, init, (idQueue,))
print(p.map(f, range(8)))
_
出力:
_[(0, 8289, 0), (1, 8290, 1), (2, 8294, 4), (3, 8291, 9), (0, 8289, 16), (1, 8290, 25), (2, 8294, 36), (3, 8291, 49)]
_
プールには4つの異なるpidしかありませんが、プールには8つのプロセスが含まれ、1つのidxは1つのプロセスでのみ使用されることに注意してください。
私はこれをスレッドで行い、最終的に a queue を使用してジョブ管理を処理しました。これがベースラインです。私の完全なバージョンには_try-catches
_の束があります(特にワーカーでは、q.task_done()
が失敗しても呼び出されることを確認します)。
_from threading import Thread
from queue import Queue
import time
import random
def run(idx, *args):
time.sleep(random.random() * 1)
print idx, ':', args
def run_jobs(jobs, workers=1):
q = Queue()
def worker(idx):
while True:
args = q.get()
run(idx, *args)
q.task_done()
for job in jobs:
q.put(job)
for i in range(0, workers):
t = Thread(target=worker, args=[i])
t.daemon = True
t.start()
q.join()
if __== "__main__":
run_jobs([('job', i) for i in range(0,10)], workers=5)
_
マルチプロセッシングを使用する必要はありませんでしたが(ワーカーは外部プロセスを呼び出すためだけです)、これを拡張することができました。マルチプロセッシング用のAPIは、それを少し変更します。次のように適応できます。
_from multiprocessing import Process, Queue
from Queue import Empty
import time
import random
def run(idx, *args):
time.sleep(random.random() * i)
print idx, ':', args
def run_jobs(jobs, workers=1):
q = Queue()
def worker(idx):
try:
while True:
args = q.get(timeout=1)
run(idx, *args)
except Empty:
return
for job in jobs:
q.put(job)
processes = []
for i in range(0, workers):
p = Process(target=worker, args=[i])
p.daemon = True
p.start()
processes.append(p)
for p in processes:
p.join()
if __== "__main__":
run_jobs([('job', i) for i in range(0,10)], workers=5)
_
両方のバージョンは次のようなものを出力します:
_0 : ('job', 0)
1 : ('job', 2)
1 : ('job', 6)
3 : ('job', 3)
0 : ('job', 5)
1 : ('job', 7)
2 : ('job', 1)
4 : ('job', 4)
3 : ('job', 8)
0 : ('job', 9)
_