2016-10-23 65 views
2

我有一個簡單的項目,我需要並行打印進度信息,例如進度條。並行打印和寫入鎖

每個小節都有一個位置,終端中的書寫指針會根據小節的位置上下移動。

這在串行完成時效果很好,但由於賽車問題而平行打印時失敗。我試圖使用multiprocessing.Lock(),但無濟於事。

這裏是我當前的代碼:

from __future__ import division 

import os, sys 
import signal 
from time import sleep 
from multiprocessing import Pool, freeze_support, Lock 

if os.name == 'nt': 
    import colorama # to support cursor up 
    colorama.init() 

_term_move_up = '\x1b[A' 

write_lock = Lock() 

class simple_bar(object): 
    def __init__(self, iterable, desc='', position=0): 
     signal.signal(signal.SIGINT, signal.SIG_IGN) # handles keyboardinterrupt 
     self.iterable = iterable 
     self.total = len(iterable) 
     self.n = 0 
     self.position = position 
     self.desc = desc 
     self.display() 

    def __iter__(self): 
     for obj in self.iterable: 
      yield obj 
      self.update() 

    def update(self, n=1): 
     self.n += n 
     self.display() 

    def display(self, fp=None, width=79): 
     if not fp: 
      fp = sys.stdout 

     with write_lock: 
      fp.write('\n' * self.position) 
      l_part = self.desc + ': ' 
      bar = l_part + '#' * int((self.n/self.total) * (width - len(l_part))) 
      fp.write('\r' + bar + ' ' * (width - len(bar))) 
      fp.write(_term_move_up * self.position) 
      fp.flush() 

def progresser(n):   
    text = "progresser #{}".format(n) 
    for i in simple_bar(range(5000), desc=text, position=n): 
     sleep(0.001) 

if __name__ == '__main__': 
    freeze_support() 
    L = list(range(3)) 
    Pool(len(L)).map(progresser, L) 

該工程確定序列替代,這給正確的輸出應該由上面的水貨版本製作:

# Same code as above, except __main__ 

if __name__ == '__main__': 
    t_list = [simple_bar(range(5000), desc="progresser #{}".format(n), position=n) for n in xrange(3)] 
    for i in range(5000): 
     for t in t_list: 
      t.update() 

我不知道是什麼出錯了。我在Windows 7

我正在尋找一種方法來並行打印安全的多處理和理想,但可選線程安全使用Python 2.7.12。

/編輯:有趣的是,如果我把一個等待(但也足夠大)打印前,那麼條好嗎印刷:

# ... 
    def display(self, fp=None, width=79): 
     if not fp: 
      fp = sys.stdout 

     with write_lock: 
      sleep(1) # this fixes the issue by adding a delay 
      fp.write('\n' * self.position) 
      l_part = self.desc + ': ' 
      bar = l_part + '#' * int((self.n/self.total) * (width - len(l_part))) 
      fp.write('\r' + bar + ' ' * (width - len(bar))) 
      fp.write(_term_move_up * self.position) 
      fp.flush() 
# ... 

我不知道這意味着什麼結論。

+0

不知道我理解正確。你想同時處理一些工作並在完成一部分工作時打印進度條嗎?進度是否由您的子流程或您的主流程打印是否重要? – noxdafox

+0

@noxdafox對於第一個問題,對於第二個問題,是應該從子進程打印進度,這是問題所在。從主流程來看,沒有問題,因爲沒有涉及到併發。 – gaborous

回答

1

這可能是全局鎖變量的問題。當你在unix中創建子進程時,你有父進程的內存副本。在windows它似乎並非如此

試試這個代碼

from __future__ import division 
import os, sys 
import signal 
from time import sleep 
from multiprocessing import Pool, freeze_support, Lock 

if os.name == 'nt': 
    import colorama # to support cursor up 
    colorama.init() 

_term_move_up = '\x1b[A' 



class simple_bar(object): 
    def __init__(self, iterable, desc='', position=0): 
     signal.signal(signal.SIGINT, signal.SIG_IGN) # handles keyboardinterrupt 
     self.iterable = iterable 
     self.total = len(iterable) 
     self.n = 0 
     self.position = position 
     self.desc = desc 
     self.display() 

    def __iter__(self): 
     for obj in self.iterable: 
      yield obj 
      self.update() 

    def update(self, n=1): 
     self.n += n 
     self.display() 

    def display(self, fp=None, width=79): 
     if not fp: 
      fp = sys.stdout 

     with write_lock: 
      fp.write('\n' * self.position) 
      l_part = self.desc + ': ' 
      bar = l_part + '#' * int((self.n/self.total) * (width - len(l_part))) 
      fp.write('\r' + bar + ' ' * (width - len(bar))) 
      fp.write(_term_move_up * self.position) 
      fp.flush() 

def progresser(n): 
    text = "progresser #{}".format(n) 
    for i in simple_bar(range(5000), desc=text, position=n): 
     sleep(0.001) 

def init_child(lock_): 
    global write_lock 
    write_lock = lock_ 

if __name__ == '__main__': 
    write_lock = Lock() 
    L = list(range(3)) 
    pool = Pool(len(L), initializer=init_child, initargs=(write_lock,)) 
    pool.map(progresser, L) 
+0

好,趕上了。該死的Windows ......但理想情況下,我想爲父母透明地管理鎖,而不需要父母提供鎖(鎖應該由孩子創建,也許是由班級創建的,或者我不知道是什麼)。你認爲這是可能的嗎? – gaborous

+0

沒有辦法,正如Alexey所說,Windows不能分叉進程但只產生它們,所以子進程無法訪問父進程的數據。我們需要將鎖從父母傳遞給孩子。另請參閱:http://stackoverflow.com/a/28721419和http://rhodesmill.org/brandon/2010/python-multiprocessing-linux-windows/ – gaborous

4

您需要在write_lock.release()之前添加fp.flush()

無關的評論:

  • 考慮使用鎖的上下文管理器(with write_lock...代替人工acquire()release()) - 這是更容易跟蹤和不易出錯。
  • 這兩個版本都無法處理中斷(Ctrl + C),您可能需要查看該中斷。
+0

感謝您的建議,但抱歉,這並未解決問題,請參閱上面的更新代碼。在我最初的代碼中,調用了fp.flush(),但我忘了將它添加到這個緊湊的版本中,對此感到抱歉,但無論如何它沒有幫助。 – gaborous