1

我正在運行一個啓動子進程以通過rsync執行備份的備份腳本。不過,我無法限制它一次啓動的rsyncs數量。限制一次從python腳本運行的進程數

這裏是我目前工作的代碼:

print "active_children: ", multiprocessing.active_children() 
print "active_children len: ", len(multiprocessing.active_children()) 
while len(multiprocessing.active_children()) > 49: 
    sleep(2) 
p = multiprocessing.Process(target=do_backup, args=(shash["NAME"],ip,shash["buTYPE"],)) 
jobs.append(p) 
p.start() 

這正顯示出最多一個孩子時,我有上百個運行rsyncs的。以下是實際啓動rsync的代碼(來自do_backup函數內部)。命令是含rsync的行一個變量:

print command 
subprocess.Popen(command, stdout=subprocess.PIPE, shell=True) 
return 1 

如果我添加一個睡眠(x)與do_backup功能會顯示爲一個活躍的孩子,而它正在睡覺。此外,進程表顯示rsync進程的PPID爲1.我假設,rsync分裂並且不再是python的子代,它允許我的子進程死掉,所以我不能再計算它了。有誰知道如何保持python小孩活着,並計數,直到rsync完成?

+0

有喲使用進程池考慮? – moooeeeep 2014-10-28 13:53:43

回答

2

讓我們澄清一些誤解第一

我從這個假設rsync的分裂關閉,不再是 孩子蟒,讓我的子進程死,所以我不能指望 它了。

rsync確實「分裂」。在UNIX系統上,這稱爲fork

當一個進程叉,創建子進程 - 所以rsync蟒蛇的孩子。這個孩子獨立於父母執行 - 同時(「同時」)執行。

一個過程可以管理自己的孩子。這裏有一些具體的syscalls,但是在談論python時有點偏離主題,它有自己的高級接口

如果你檢查subprocess.Popen's documentation,你會注意到它根本不是一個函數調用:它是一類。通過調用它,您將創建該類的一個實例 - 一個Popen object。這種對象有多種方法。特別是,wait將允許你的父進程(python)block,直到子進程終止。記住


利用這一點,讓我們看看你的代碼,並把它簡化一下:

p = multiprocessing.Process(target=do_backup, ...) 

在這裏,你實際上分叉並創建子進程。 這個過程是另一個python解釋器(與所有的multiprocessing進程一樣),並執行do_backup函數。

def do_backup() 
    subprocess.Popen("rsync ...", ...) 

在這裏,你是分叉再次。您將創建另一個流程(rsync),並讓它在「後臺」中運行,因爲您不是wait


隨着所有這一切的清理,我希望你能看到一種方式與你現有的代碼。如果你想減少它的複雜性,我建議你檢查並修改JoErNanO的答案,它會重用multiprocessing.Pool來自動跟蹤過程。

你決定要追求哪種方式,應避免與Popen分叉創建rsync過程 - 爲創建的另一種方法不必要的。相反,檢查os.execv,這取代當前進程與另一

+0

謝謝,將.wait()添加到Popen啓動rsync解決了這個問題。 – MVanOrder 2014-10-28 14:51:47

5

多處理池的

你有沒有想過使用multiprocessing.Pool的?這些允許您定義固定數量的工作進程,用於執行所需的工作。這裏的關鍵是在固定號碼這將讓你完全控制你將啓動多少個rsync實例。

看着我掛文檔中提供的例子,首先你宣佈nPool一個進程,然後你可以決定是否要map()apply()(連同其各自_async()兄弟姐妹)作業到池。

from multiprocessing import Pool 

def f(x): 
    return x*x 

if __name__ == '__main__': 
    pool = Pool(processes=4)    # start 4 worker processes 

    pool.apply_async(f, (10,)) # evaluate "f(10)" asynchronously 
    ... 
    pool.map(f, range(10)) 

這裏的明顯優勢是,你將永遠不會意外叉式轟炸你的機器,你會只產卵請求n過程。

運行你的rsync然後

你的過程中產卵的代碼將成爲類似:

from multiprocessing import Pool 

def do_backup(arg1, arg2, arg3, ...): 
    # Do stuff 

if __name__ == '__main__': 
    # Start a Pool with 4 processes 
    pool = Pool(processes=4) 
    jobs = [] 

    for ... : 
     # Run the function 
     proc = pool.apply_async(func=do_backup, args=(shash["NAME"],ip,shash["buTYPE"],)) 
     jobs.append(proc) 

    # Wait for jobs to complete before exiting 
    while(not all([p.ready() for p in jobs])): 
     time.sleep(5) 

    # Safely terminate the pool 
    pool.close() 
    pool.join() 
+0

你錯過了讓他實際運行一個非python解釋器進程(如'rsync')的魔法:'os.execv'或類似 – goncalopp 2014-10-28 14:05:19

+0

@goncalopp我假設代碼的位已經存在於OP的'do_backup()'函數中。 :) – JoErNanO 2014-10-28 14:15:44

+0

你的答案比我的Pythonic +1 – 2014-10-28 14:18:35

0

這不是多線程,但多。我假設你在Unix系統上,如果你使用的是rsync,雖然我確信它可以在Windows系統上運行。爲了控制產卵的子女過程的死亡,你必須fork他們。

在Python here中有一個很好的問題。

+0

注意'subprocess.Popen'正在他的代碼中進行分支。 'fork'是非常低的級別,並且在python代碼中很少見 - 有更高級別的庫可以幫助完成這類任務(例如JoErNanO提到的''multiprocessing'' – goncalopp 2014-10-28 14:01:38