2010-05-23 59 views
3

我一直在研究需要管理外部流程的gui應用程序。與外部進程一起工作會導致很多問題,導致程序員的生活困難。我覺得這個應用程序的維護時間長得令人無法接受。我一直在試圖列舉使外部流程工作困難的事情,以便我可以想出減輕痛苦的方法。這種轉變成我認爲我會在這裏發佈的咆哮,以獲得一些反饋意見,併爲任何想要進入這些非常黑暗的水域的人提供一些指導。以下是我目前爲止所得到的結果:處理外部流程

  1. 孩子的輸出可能會與父母的輸出混淆。這可能會導致兩種輸出誤導並難以閱讀。可能很難說出來自哪裏。當事物異步時,弄清楚發生了什麼變得更加困難。這裏是一個人爲的例子:

    import textwrap, os, time 
    from subprocess import Popen 
    test_path = 'test_file.py' 
    
    with open(test_path, 'w') as file: 
        file.write(textwrap.dedent(''' 
         import time 
         for i in range(3): 
          print 'Hello %i' % i 
          time.sleep(1)''')) 
    
    proc = Popen('python -B "%s"' % test_path) 
    
    for i in range(3): 
        print 'Hello %i' % i 
        time.sleep(1) 
    
    os.remove(test_path) 
    

    輸出:

    Hello 0 
    Hello 0 
    Hello 1 
    Hello 1 
    Hello 2 
    Hello 2 
    

    我想我可以有子進程的輸出寫入到一個文件。但是每次我想看到一個打印語句的結果時,必須打開一個文件可能會很麻煩。

    如果我有子進程的代碼,我可以添加一個標籤,如print 'child: Hello %i',但是對於每次打印都可能會這樣做。它增加了一些噪音輸出。當然,如果我無法訪問代碼,我無法做到這一點。

    我可以手動管理過程輸出。但是,然後你用線程和輪詢以及類似的東西來打開一大堆蠕蟲。

    一個簡單的解決方案是處理像同步函數這樣的進程,也就是說,在進程完成之前不再執行代碼。換句話說,使過程阻塞。但是,如果你正在構建一個gui應用程序,那不起作用。這給我帶來了下一個問題...

  2. 阻塞過程導致GUI無響應。

    import textwrap, sys, os 
    from subprocess import Popen 
    
    from PyQt4.QtGui import * 
    from PyQt4.QtCore import * 
    
    test_path = 'test_file.py' 
    with open(test_path, 'w') as file: 
        file.write(textwrap.dedent(''' 
         import time 
         for i in range(3): 
          print 'Hello %i' % i 
          time.sleep(1)''')) 
    
    app = QApplication(sys.argv) 
    button = QPushButton('Launch process') 
    def launch_proc(): 
        # Can't move the window until process completes 
        proc = Popen('python -B "%s"' % test_path) 
        proc.communicate() 
    button.connect(button, SIGNAL('clicked()'), launch_proc) 
    button.show() 
    app.exec_() 
    os.remove(test_path) 
    

    Qt提供其自身的過程稱爲包裝其QProcess可以在這方面幫助。您可以將函數連接到信號以相對容易地捕獲輸出。這是我目前使用的。但是我發現所有這些信號都像goto聲明一樣可疑,並且可能導致意大利麪代碼。我想我想通過讓QProcess的'finished'信號調用一個包含進程調用之後的所有代碼的函數來獲得排序阻塞行爲。我認爲這應該工作,但我仍然有點模糊的細節...

  3. 當您從子進程返回到父進程時,堆棧跟蹤會中斷。如果一個正常的函數擰緊,你會得到一個很好的完整的堆棧跟蹤文件名和行號。如果一個子進程崩潰了,如果你有任何輸出的話,你會很幸運。每當出現問題時,你最終不得不做更多的偵探工作。

  4. 說起來,輸出在處理外部過程時有一種消失的方式。就像如果你通過windows'cmd'命令運行某些東西,控制檯會彈出,執行代碼,然後在你有機會看到輸出之前消失。你必須通過/ k標誌來使它堅持下去。類似的問題似乎總是出現。

    我想這兩個問題3和4都有相同的根本原因:沒有異常處理。異常處理旨在用於功能,它不適用於流程。也許有一些方法可以爲流程的異常處理提供一些東西?我想這就是stderr的用途?但處理兩個不同的流本身可能很煩人。也許我應該看看這更多...

  5. 進程可以掛起並堅持在背景中沒有你意識到它。所以你最終會對你的電腦大喊大叫,因爲它會變得很慢,直到你最終調出任務管理器,並在後臺看到30個相同進程的實例。

    此外,掛起的後臺進程可以以各種有趣的方式干擾進程的其他實例,例如通過持有文件句柄或類似的東西來引起權限錯誤。

    這似乎是一個簡單的解決方案,如果子進程沒有關閉它自己,父進程會在退出時終止子進程。但是,如果父級進程崩潰,清理代碼可能不會被調用,並且子級可能被掛起。另外,如果父母等待孩子完成,並且孩子處於無限循環或某種情況,那麼最終可能會有兩個掛起的過程。

    此問題可能會導致問題2的額外樂趣,導致您的GUI完全停止響應並強制您使用任務管理器殺死所有內容。

  6. ,F ***以外ING報價

    參數往往需要傳遞給進程。這本身就令人頭疼。特別是如果你正在處理文件路徑。說...'C:/我的文檔/無論/'。如果您沒有引號,則字符串通常會在空格處被拆分並被解釋爲兩個參數。如果你需要嵌套引號,你可以使用'和',但如果你需要使用兩層以上的引用,你必須做一些令人討厭的轉義,例如:「cmd/k'python \'path 1 \'\路徑2 \「」」。

    很好的解決了這個問題傳遞參數的列表,而不是作爲一個字符串。子過程可以讓你做到這一點。

  7. 不能輕易地從一個返回數據子進程

    當然你也可以使用stdout但是如果你想爲了調試目的而在這裏打印一個print呢如果它希望輸出的格式是某種特定的方式,並返回另一個前夕一切正常。

  8. 模糊的命令行標誌和糟糕的基於終端的幫助系統。

    這些是我在使用os級應用程序時經常遇到的問題。就像我提到的/ k標誌一樣,爲了讓一個cmd窗口打開,誰的想法是?在這方面,Unix應用程序往往不太友善。希望你可以使用谷歌或StackOverflow找到你需要的答案。但是,如果沒有,你會有很多無聊的閱讀和令人費解的嘗試和錯誤。

  9. 外部因素。

    這是一種模糊的。但是當你離開你自己腳本的相對庇護港來處理外部過程時,你會發現自己不得不在更大程度上處理「外部世界」。這是一個可怕的地方。各種各樣的事情可能會出錯。只是給一個隨機的例子:一個進程運行的cwd可以修改它的行爲。

可能還有其他問題,但那些是我迄今寫下來的問題。還有其他的問題想要補充嗎?有沒有處理這些問題的建議?

回答

2

檢出子流程模塊。它應該有助於輸出分離。我沒有看到單獨的輸出流或單個流中的某種輸出標籤。

懸掛過程問題也很困難。我能夠做出的唯一解決方案就是在外部過程中設置一個計時器,如果它沒有按照規定的時間返回,就殺了它。粗糙,討厭,如果其他人有一個很好的解決方案,我很樂意聽到它,所以我也可以使用它。

你可以做的一件事來幫助處理完全無法管理的關機問題,就是保留一個pid文件的目錄。每當你啓動一個外部進程時,將一個文件寫入你的pid文件目錄,其名稱是該進程的pid。當您知道該進程已完全退出時,請清除該pid文件。您可以使用pid目錄中的內容來幫助清理崩潰或重新啓動。

這可能不會提供任何滿意的或有用的答案,但也許這是一個開始。

+0

一些不錯的想法,謝謝。我想我會把這個視爲已被接受的觀察,因爲沒有人會迴應。我有點驚訝這個問題沒有得到更多的關注。哦,我想這只是表明沒有任何簡單的解決方案。 – 2010-05-24 22:44:09