在python多处理池中获取worker的唯一ID

时间:2012-04-17 12:11:09

标签: python multiprocessing

有没有办法为python多处理池中的每个worker分配一个唯一的ID,使得池中特定worker正在运行的作业可以知道哪个worker正在运行它?根据文档,Processname

  

该名称是一个字符串,仅用于识别目的。它没有   语义。多个进程可以使用相同的名称。

对于我的特定用例,我想在一组四个GPU上运行一堆作业,并且需要设置作业应该运行的GPU的设备编号。因为作业的长度不均匀,我想确保在上一个作业完成之前尝试在其上运行的作业的GPU上没有发生冲突(因此这不能预先为ID分配ID提前工作单位)。

5 个答案:

答案 0 :(得分:56)

看起来你想要的很简单: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 - 在cpython中,这是进程对象的内存地址,因此我认为没有任何重叠的可能性。最后,您可以使用流程的identpid属性 - 但只有在流程启动后才会设置。

此外,查看源代码,在我看来,很可能自动生成的名称(如上面Process repr字符串中的第一个值所示)是唯一的。 multiprocessing为每个进程维护一个itertools.counter对象,用于为其生成的任何子进程生成_identity元组。因此,顶级进程使用单值id生成子进程,并且它们使用双值id生成进程,依此类推。然后,如果没有名称传递给Process构造函数,则只需autogenerates the name基于_ {1},使用':'.join(...)。然后Pool alters the name使用replace进程,让自动生成的ID保持不变。

所有这一切的结果是虽然两个Process es 可能具有相同的名称,因为可能在创建时为它们指定相同的名称它们,如果您不触摸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]

答案 1 :(得分:3)

您可以使用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个进程,而一个idx仅由一个进程使用。

答案 2 :(得分:1)

我使用线程进行了此操作,最后使用a queue来处理作业管理。这是基线。我的完整版本有一堆try-catches(特别是在worker中,以确保即使在失败时调用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 __name__ == "__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 __name__ == "__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)

答案 3 :(得分:0)

我不确定它如何与Pool一起工作,但是打印Process会产生一些独特的输出:

x = Process(target=time.sleep, args=[20])
x.start()
print(x)  # <Process name='Process-5' pid=97121 parent=95732 started>

答案 4 :(得分:0)

我设法通过使用getattr获取函数句柄,然后使用包装器包装和解包想要传递给要映射的方法的参数来映射到类方法。就我而言,我是从启动池的同一类传递方法的,但是您也可以传递一个对象以映射到不同的类。

这是代码:

import multiprocessing
from multiprocessing import Pool


def warp(args):
    func = args[0]
    frame = args[1]
    left_over = args[2:]
    func(frame, *left_over)


class MyClass:

    def __init__(self):
        self.my_flag = 5

    def exec_method(self, method, int_list, *args):
        obj = getattr(self, method.__name__)

        packed = list()
        for i in int_list:
            pack = list()
            pack.append(obj)
            pack.append(i)
            for arg in args:
                pack.append(arg)
            packed.append(pack)

        print("Start")
        pool = Pool(processes=multiprocessing.cpu_count())
        pool.map(warp, packed)
        print("End")

    def method1(self, my_str):
        print(self.my_flag, my_str)

    def method2(self, i, print_str, bool_flat):
        print(multiprocessing.current_process(), self.my_flag, i, print_str, str(bool_flat))


cls: MyClass = MyClass()
cls.my_flag = 58
cls.exec_method(cls.method2, [1, 5, 10, 20, 30], "this is a string", True)

这是输出:

Start
<ForkProcess(ForkPoolWorker-1, started daemon)> 58 1 this is a string True
<ForkProcess(ForkPoolWorker-2, started daemon)> 58 5 this is a string True
<ForkProcess(ForkPoolWorker-4, started daemon)> 58 20 this is a string True
<ForkProcess(ForkPoolWorker-5, started daemon)> 58 30 this is a string True
<ForkProcess(ForkPoolWorker-3, started daemon)> 58 10 this is a string True
End