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ステートメントの結果を確認するたびにファイルを開かなければならないのは面倒な場合があります。

    子プロセスのコードがある場合は、のようなラベルを追加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の目的だと思いますか?しかし、2つの異なるストリームを処理すること自体が煩わしい場合があります。多分私はこれをもっと調べる必要があります...

  5. プロセスは、気付かないうちにバックグラウンドでハングしたり、立ち往生したりする可能性があります。そのため、最終的にタスクマネージャーを起動して、同じプロセスの30個のインスタンスがバックグラウンドでぶら下がっているのを確認するまで、コンピューターに怒鳴りつけてしまいます。

    また、バックグラウンドプロセスをぶら下げると、ファイルへのハンドルを保持するなどしてアクセス許可エラーが発生するなど、さまざまな楽しい方法でプロセスの他のインスタンスと相互参照できます。

    これに対する簡単な解決策は、子プロセスがそれ自体を閉じなかった場合に、親プロセスに終了時に子プロセスを強制終了させることであるように思われます。ただし、プロセスがクラッシュした場合、クリーンアップコードが呼び出されず、子がハングしたままになる可能性があります。

    また、親が子の完了を待機していて、子が無限ループなどにある場合、2つのハングプロセスが発生する可能性があります。

    この問題は、問題2に関連してさらに楽しくなり、GUIが完全に応答しなくなり、タスクマネージャーですべてを強制終了する可能性があります。

  6. F***ing引用符

    多くの場合、パラメータをプロセスに渡す必要があります。これはそれ自体が頭痛の種です。特にファイルパスを扱っている場合。言ってください...'C:/ My Documents /whatever/'。引用符がない場合、文字列はスペースで分割され、2つの引数として解釈されることがよくあります。ネストされた引用符が必要な場合は、'と"を使用できます。ただし、2層以上の引用符を使用する必要がある場合は、たとえば、" cmd / k'python \'path 1\'\'のように厄介なエスケープを行う必要があります。パス2\''"。

    この問題の良い解決策は、パラメータを単一の文字列としてではなくリストとして渡すことです。サブプロセスを使用すると、これを行うことができます。

  7. サブプロセスからデータを簡単に返すことはできません。

    もちろん、stdoutを使用することもできます。しかし、デバッグの目的でそこにプリントを入れたい場合はどうでしょうか?特定の方法でフォーマットされた出力を期待している場合、それは親を台無しにするでしょう。関数では、1つの文字列を出力して別の文字列を返すことができ、すべてが正常に機能します。

  8. あいまいなコマンドラインフラグと安っぽい端末ベースのヘルプシステム。

    これらは、OSレベルのアプリを使用するときによく遭遇する問題です。私が言及した/kフラグのように、cmdウィンドウを開いたままにするために、誰の考えでしたか?Unixアプリは、この点でそれほど友好的ではない傾向があります。うまくいけば、あなたはあなたが必要とする答えを見つけるためにグーグルまたはStackOverflowを使うことができます。しかし、そうでない場合は、退屈な読書とイライラする試行錯誤がたくさんあります。

  9. 外部要因。

    これはちょっとあいまいです。しかし、外部プロセスを処理するために自分のスクリプトの比較的保護された港を離れると、「外の世界」をはるかに広範囲に処理する必要があります。そして、それは恐ろしい場所です。あらゆる種類のことがうまくいかない可能性があります。ランダムな例を挙げると、プロセスが実行されるcwdは、プロセスの動作を変更できます。

おそらく他の問題もありますが、それは私がこれまでに書き留めたものです。追加したい他の障害はありますか?これらの問題に対処するための提案はありますか?

4

1 に答える 1

2

サブプロセスモジュールを確認してください。出力の分離に役立つはずです。個別の出力ストリームまたは単一のストリーム内のある種の出力タグ付けを回避する方法がわかりません。

吊り下げプロセスの問題も困難です。私が作成できた唯一の解決策は、外部プロセスにタイマーを設定し、割り当てられた時間内にタイマーが戻らない場合はタイマーを強制終了することです。粗野で厄介で、他の誰かが良い解決策を持っているなら、私もそれを使うことができるようにそれを聞いてみたいです。

完全に管理されていないシャットダウンの問題に対処するためにできることの1つは、pidファイルのディレクトリを保持することです。外部プロセスを開始するときはいつでも、プロセスのpidである名前でファイルをpidファイルディレクトリに書き込みます。プロセスが正常に終了したことがわかったら、pidファイルを消去します。pidディレクトリにあるものを使用して、クラッシュまたは再起動時のクリーンアップに役立てることができます。

これは満足のいく、または有用な答えを提供しないかもしれませんが、おそらくそれは始まりです。

于 2010-05-23T20:48:52.813 に答える