2017-09-05 57 views
0

我使用的是python 2.7,並且沒有升級或移回subprocess32的選項。我在一個線程環境中使用它,通常它工作正常,但有時subprocess創建沒有返回,因此線程掛起,即使strace在掛起的情況下不起作用,所以我沒有得到任何反饋。子過程不是線程安全的,替代方法?

E.G.這條線可能會導致掛起(返回的數據是小,所以它不是一個管道的問題):

process = subprocess.Popen(cmd, 
      stdout=subprocess.PIPE, 
      stderr=subprocess.STDOUT) 

我後來讀到子在Python 2.7,並且不是線程安全的「各種問題」,固定成最新版本。我正在使用多個線程調用子進程。

我已經證明了這個問題,下面的代碼(如一個顯而易見的例子 - 不是我的實際代碼),這將啓動多個線程與subprocess每個:

import os, time, threading, sys 
from subprocess import Popen 
i=0 
class Process: 
    def __init__(self, args): 
     self.args = args 

    def run(self): 
     global i 
     retcode = -1 
     try: 
       self.process = Popen(self.args) 
       i+=1 
       if i == 10: 
        sys.stdout.write("Complete\n") 
       while self.process.poll() is None: 
        time.sleep(1.0) 
       retcode = self.process.returncode 
     except: 
      sys.stdout.write("ERROR\n") 

     return retcode 

def main(): 
    processes = [Process(["/bin/cat"]) for _ in range(10)] 
    # start all processes 
    for p in processes: 
     t = threading.Thread(target=Process.run, args=(p,)) 
     t.daemon = True 
     t.start() 
    sys.stdout.write("all threads started\n") 
    # wait for Ctrl+C 
    while True: 
     time.sleep(1.0) 

main() 

這往往會導致1個或多個subprocess電話永不返回。有沒有人有關於此或解決方案/替代方案的更多信息

我正在考慮使用棄用的commands.getoutput來代替,但不知道這是否是線程安全的?它似乎對上面的代碼正確工作。

+0

引用子進程錯誤? –

+2

@JohnZwinck:這是一個:http://bugs.python.org/issue2320(還有更多,他們很容易找到)。 – NPE

+0

FWIW,我個人傾向於使用subprocess32(https://pypi.python.org/pypi/subprocess32/),但它聽起來像它不是您的環境中的選項? – NPE

回答

2

如果大部分線程正在做的是等待子進程,那麼可以通過協程來更有效地實現這一點。隨着python2你會用發電機inplement此所以對run功能進行必要的修改有:

  1. 取代time.sleep(1.0)yield把控制傳遞到另一個例程
  2. self.retcode = retcode更換return retcode或類似的,因爲發電機不能返回價值before python3.3

然後main功能可能是這樣的:

def main(): 
    processes = [Process(["/bin/cat"]) for _ in range(10)] 
    #since p.run() is a generator this doesn't run any of the code yet 
    routines = [p.run() for p in processes] 
    while routines: 
     #iterate in reverse so we can remove routines while iterating without skipping any 
     for routine in reversed(routines): 
      try: 
       next(routine) #continue the routine to next yield 
      except StopIteration: 
       #this routine has finished, we no longer need to check it 
       routines.remove(routine) 

這是爲了給你一個地方開始,我建議圍繞收益增加print聲明或使用pythontutor來更好地理解執行順序。

這樣做的好處是永遠不會有任何線程在等待任何事情,只需要一個線程在一段時間內執行一段處理,這樣可以使許多空閒線程更高效。