简体   繁体   English

如果 Python 多处理池中有 4 个子进程,如何确保所有工作人员至少运行一次任务

[英]If there are 4 child process in Python Multiprocessing Pool, How can I make sure all worker run task at least one time

I used multiprocessing pool in Python3 to connect database(Postgresql).I created database connections with multiprocessing.Pool(8,init_dbconn),and do some dml with pool.map(update, records).Obviously I can get better performance if I do commit after pool.map rather than each iteration in pool.map().So I define another function to call dbconnection.commit,and run pool.map(func_commit, range(8), 1) after pool.map(update),but I found the pool don't distribute tasks to each child process at least one time, even I increase the size or range() to do more iteration.我在 Python3 中使用多处理池连接数据库(Postgresql)。我使用 multiprocessing.Pool(8,init_dbconn) 创建了数据库连接,并使用 pool.map(update, records) 进行了一些 dml。显然,如果我这样做,我可以获得更好的性能在 pool.map 之后提交,而不是 pool.map() 中的每次迭代。所以我定义了另一个 function 来调用 dbconnection.commit,并在 pool.map(update) 之后运行 pool.map(func_commit, range(8), 1),但我发现池至少一次不会将任务分配给每个子进程,即使我增加 size 或 range() 以进行更多迭代。 So some of database connection didn't commit the dml.所以一些数据库连接没有提交dml。 Is there any method to achieve this function?有什么方法可以实现这个function?

The pseudo-code like this:伪代码如下:

def conn_db():
    try:
        conn = psycopg2.connect(database="dbname", user="username", password="pass", host="127.0.0.1", port="5432")
        return (conn)
    except:
        print(traceback.format_exc())

def init_proc():
    global conn
    conn = conn_db()
    global cursor
    cursor = conn.cursor()


def double2(i):
    print ("I'm process:%s, %s" % (os.getpid(), multiprocessing.current_process()))
    return i*2

    
def exec_ten_parallel(num_parallel, records):
    try:
        pool = Pool(8, initializer=init_proc)
        t1_b = time.time()  
        pool.imap(double2, range(16), 1)
        #for i in range(16):
        #    pool.apply_async(double2,(1,))
        pool.close()
        pool.join()
        t1_runtime = time.time() - t1_b
        #print('update all count:', len(records), 'seconds:', t1_runtime)
    except:
        print(traceback.format_exc()) 

I print the worker name and pid, as shown below我打印worker name和pid,如下图

I'm process:53141, <ForkProcess(ForkPoolWorker-1, started daemon)>
I'm process:53141, <ForkProcess(ForkPoolWorker-1, started daemon)>
I'm process:53142, <ForkProcess(ForkPoolWorker-2, started daemon)>
I'm process:53141, <ForkProcess(ForkPoolWorker-1, started daemon)>
I'm process:53141, <ForkProcess(ForkPoolWorker-1, started daemon)>
I'm process:53142, <ForkProcess(ForkPoolWorker-2, started daemon)>
I'm process:53141, <ForkProcess(ForkPoolWorker-1, started daemon)>
I'm process:53141, <ForkProcess(ForkPoolWorker-1, started daemon)>
[root@dc3-06-005 dml-test]# python3 bak-p.py 
I'm process:53164, <ForkProcess(ForkPoolWorker-3, started daemon)>
I'm process:53164, <ForkProcess(ForkPoolWorker-3, started daemon)>
I'm process:53164, <ForkProcess(ForkPoolWorker-3, started daemon)>
I'm process:53164, <ForkProcess(ForkPoolWorker-3, started daemon)>
I'm process:53164, <ForkProcess(ForkPoolWorker-3, started daemon)>
I'm process:53164, <ForkProcess(ForkPoolWorker-3, started daemon)>
I'm process:53164, <ForkProcess(ForkPoolWorker-3, started daemon)>
I'm process:53164, <ForkProcess(ForkPoolWorker-3, started daemon)>

Almost could not run all ForkPoolWorker, just only call one or two worker do job.几乎无法运行所有的 ForkPoolWorker,只能调用一两个 worker 来做作业。

how about using multiprocessing.Barrier , you pass it in the initializer with the number of processes in the pool, then inside the function just call the wait() method of it, just make sure the function has chunksize=1 and is called exactly the number of child processes, orelse everything will hang.使用multiprocessing.Barrier怎么样,你在初始化器中传递池中的进程数,然后在 function 中调用它的wait()方法,只要确保 function 有chunksize=1并且被称为子进程的数量,否则一切都会挂起。

to avoid the hanging, you can pass a timeout, but you will also have to catch the error that will be raised if the timeout runs out... so its not the safest approach, but totally doable.为了避免挂起,您可以通过超时,但是如果超时用完,您还必须捕获将引发的错误......所以它不是最安全的方法,但完全可行。

from multiprocessing import Pool
import time
import traceback
import os
import multiprocessing

def double2(i):
    global gbarrier
    print ("I'm process:%s, %s" % (os.getpid(), multiprocessing.current_process()))
    gbarrier.wait()
    return i*2

def init_func(barrier):
    global gbarrier
    gbarrier = barrier

def exec_ten_parallel():
    try:
        barrier = multiprocessing.Barrier(8)
        pool = Pool(8,initializer=init_func,initargs=(barrier,))
        t1_b = time.time()
        pool.imap(double2, range(8), chunksize=1)
        #for i in range(16):
        #    pool.apply_async(double2,(1,))
        pool.close()
        pool.join()
        t1_runtime = time.time() - t1_b
        #print('update all count:', len(records), 'seconds:', t1_runtime)
    except:
        print(traceback.format_exc())

if __name__ == "__main__":
    exec_ten_parallel()
I'm process:6396, <SpawnProcess name='SpawnPoolWorker-2' parent=12688 started daemon>
I'm process:13612, <SpawnProcess name='SpawnPoolWorker-4' parent=12688 started daemon>
I'm process:14848, <SpawnProcess name='SpawnPoolWorker-8' parent=12688 started daemon>
I'm process:6836, <SpawnProcess name='SpawnPoolWorker-1' parent=12688 started daemon>
I'm process:12632, <SpawnProcess name='SpawnPoolWorker-6' parent=12688 started daemon>
I'm process:1536, <SpawnProcess name='SpawnPoolWorker-3' parent=12688 started daemon>
I'm process:11856, <SpawnProcess name='SpawnPoolWorker-5' parent=12688 started daemon>
I'm process:13888, <SpawnProcess name='SpawnPoolWorker-7' parent=12688 started daemon>

if you want a safer approach, then perhaps for the normal runtime have a counter on every process that will commit every 100 operations or something, and only call this dangerous function when you absolutely have to, in short it should work out of the box, but inside the production environment you must handle the "one time it will fail because a cosmic ray crashed a process before it reached the barrier" sort of error, otherwise you must make your own multiprocess.Pool using multiple multiprocess.Process如果您想要一种更安全的方法,那么对于正常的运行时,可能在每个将提交每 100 次操作或其他操作的进程上都有一个计数器,并且只有在绝对必须时才调用这个危险的 function,简而言之,它应该开箱即用,但是在生产环境中,您必须处理“有一次它会失败,因为宇宙射线在到达障碍之前使进程崩溃”之类的错误,否则您必须使用多个multiprocess.Process创建自己的multiprocess.Pool

Edit: moved the wait to the end of function instead of its start, for faster out-of-order execution, also imap is fine if you don't want to block the main process.编辑:将等待移动到 function 的末尾而不是它的开始,以便更快的乱序执行,如果您不想阻止主进程,imap 也可以。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM