2013-11-14 435 views
5

我正在嘗試創建一個程序使用多個進程,我想幹淨地終止所有派生進程,如果發生錯誤。下面我寫了一些僞類型代碼,我認爲我需要做的事情,但我不知道什麼是最好的方法是與所有進程通信,發生錯誤,並且應該終止。乾淨的Python多進程終止依賴於退出標誌

我認爲我應該爲這類事情使用類,但是我對Python很陌生,所以我只是試圖讓我的頭在基礎知識第一。

#imports 

exitFlag = True 

# Function for threads to process 
def url_thread_worker(): 
# while exitFlag: 
    try: 
     # do something 
    except: 
     # we've ran into a problem, we need to kill all the spawned processes and cleanly exit the program 
     exitFlag = False 

def processStarter(): 

    process_1 = multiprocessing.Process(name="Process-1", target=url_thread_worker, args=()) 
    process_2 = multiprocessing.Process(name="Process-2", target=url_thread_worker, args=()) 

    process_1.start() 
    process_2.start() 


if __name__ == '__main__': 
    processStarter() 

在此先感謝

回答

4

這裏是我的建議:

import multiprocessing 
import threading 
import time 

def good_worker(): 
    print "[GoodWorker] Starting" 
    time.sleep(4) 
    print "[GoodWorker] all good" 

def bad_worker(): 
    print "[BadWorker] Starting" 
    time.sleep(2) 
    raise Exception("ups!") 

class MyProcManager(object): 
    def __init__(self): 
     self.procs = [] 
     self.errors_flag = False 
     self._threads = [] 
     self._lock = threading.Lock() 

    def terminate_all(self): 
     with self._lock: 
      for p in self.procs: 
       if p.is_alive(): 
        print "Terminating %s" % p 
        p.terminate() 

    def launch_proc(self, func, args=(), kwargs= {}): 
     t = threading.Thread(target=self._proc_thread_runner, 
          args=(func, args, kwargs)) 
     self._threads.append(t) 
     t.start() 

    def _proc_thread_runner(self, func, args, kwargs): 
     p = multiprocessing.Process(target=func, args=args, kwargs=kwargs) 
     self.procs.append(p) 
     p.start() 
     while p.exitcode is None: 
      p.join() 
     if p.exitcode > 0: 
      self.errors_flag = True 
      self.terminate_all() 

    def wait(self): 
     for t in self._threads: 
      t.join() 

if __name__ == '__main__': 
    proc_manager = MyProcManager() 
    proc_manager.launch_proc(good_worker) 
    proc_manager.launch_proc(good_worker) 
    proc_manager.launch_proc(bad_worker) 
    proc_manager.wait() 
    if proc_manager.errors_flag: 
     print "Errors flag is set: some process crashed" 
    else: 
     print "Everything closed cleanly" 

你需要爲每個進程運行的包裝線,即等待其結束。 當一個進程結束時,檢查exitcode:if> 0,意味着它引發了一些未處理的異常。現在調用terminate_all()來關閉所有剩餘的活動進程。 包裝線程也將完成,因爲它們依賴於進程運行。

此外,在您的代碼中,您可以隨時根據需要調用proc_manager.terminate_all()。你可以檢查一些不同的線程或類似的標誌..

希望它對你的情況很好。在你的原始代碼中,你做了一些像全局的exit_flag:在多處理中你永遠不會有一個「全局的」exit_flag,因爲它不是全局的,因爲你正在使用分離的內存空間分離進程。這隻適用於可以共享狀態的線程環境。如果你在多處理中需要它,那麼你必須在進程之間有明確的通信(Pipe and Queue accomplish that)或者類似shared memory objects

+0

這是一個很好的例子,幫助了很多。非常感謝你。 –

+1

你選擇了處理「容易的部分」 - 當孩子死亡。但是,如果父母死了呢?比你會留下孤立的進程(工人)。至少對我而言,這是一個大問題。 –

+0

這是一個非常不同的情況下解決,而不是真正常見的,恕我直言。您應該對系統進行編碼以確保父代對錯誤具有適應性,並且至少可以在出現問題時終止子進程。 – abranches

1

如果你希望子進程在父進程退出時自動終止;你可以讓它們爲守護神(在.start()之前設置.daemon=True),即,如果父母檢測到錯誤;它可能會放棄 - 孩子們會得到照顧。

如果你想要孩子自己清理;你可以use multiprocessing.Event() as a global flag

import multiprocessing 

def event_func(event): 
    print '\t%r is waiting' % multiprocessing.current_process() 
    event.wait() 
    print '\t%r has woken up' % multiprocessing.current_process() 

if __name__ == '__main__': 
    event = multiprocessing.Event() 

    processes = [multiprocessing.Process(target=event_func, args=(event,)) 
       for i in range(5)] 

    for p in processes: 
     p.start() 

    print 'main is sleeping' 
    time.sleep(2) 

    print 'main is setting event' 
    event.set() 

    for p in processes: 
     p.join()