7

実行する System.Diagnotics.Processes がいくつかあります。それらの close メソッドを自動的に呼び出したいと思います。どうやら「using」キーワードが私のためにこれを行います。

これは using キーワードを使用する方法ですか?

foreach(string command in S) // command is something like "c:\a.exe"
{
    try
    {
        using(p = Process.Start(command))
        {
            // I literally put nothing in here.
        }
    }
    catch (Exception e)
    {
        // notify of process failure
    }
}

複数のプロセスを同時に実行するために開始したいと思います。

4

3 に答える 3

15
using(p = Process.Start(command))

Processこれは、クラスが実装するのでコンパイルされIDisposableますが、実際にはCloseメソッドを呼び出したいと考えています。
ロジックでは、Disposeメソッドがユーザーを呼び出すと考えCloseられます。リフレクターを使用して CLR を掘り下げると、実際にメソッドがこれを行っていることがわかります。ここまでは順調ですね。

再びリフレクターを使用して、このCloseメソッドが何を行うかを調べました。このメソッドは、基になるネイティブの win32 プロセス ハンドルを解放し、いくつかのメンバー変数をクリアします。これ (外部リソースの解放) は、まさにIDisposable パターンが行うべきことです。

ただし、これがここで達成したいことかどうかはわかりません。

基になるハンドルを解放すると、単にウィンドウに「この他のプロセスの追跡にはもう興味がありません」と通知されます。実際に他のプロセスを終了させたり、プロセスを待機させたりすることは決してありません。

それらを強制的に終了させたい場合は、プロセスでp.Kill()メソッドを使用する必要があります - ただし、プロセスを強制終了することは決して良い考えではありません。すぐ。

それらが自然に終了するのを待ちたい場合は、を使用できますp.WaitForExit()- ただし、これは一度に 1 つのプロセスを待っている場合にのみ機能します。それらすべてを同時に待ちたい場合は、注意が必要です。

通常、これには を使用しますが、からオブジェクトWaitHandle.WaitAllを取得する方法がないため、これを行うことはできません (真剣に、wtf はマイクロソフトの考えでしたか?)。WaitHandleSystem.Diagnostics.Process

プロセスごとにスレッドをスピンアップし、それらのスレッドで `WaitForExit を呼び出すこともできますが、これも間違った方法です。

WaitForMultipleObjects代わりに、p/invoke を使用してネイティブの win32関数にアクセスする必要があります。

これがサンプルです(私がテストし、実際に動作します)

[System.Runtime.InteropServices.DllImport( "kernel32.dll" )]
static extern uint WaitForMultipleObjects( uint nCount, IntPtr[] lpHandles, bool bWaitAll, uint dwMilliseconds );

static void Main( string[] args )
{
    var procs = new Process[] {
        Process.Start( @"C:\Program Files\ruby\bin\ruby.exe", "-e 'sleep 2'" ),
        Process.Start( @"C:\Program Files\ruby\bin\ruby.exe", "-e 'sleep 3'" ),
        Process.Start( @"C:\Program Files\ruby\bin\ruby.exe", "-e 'sleep 4'" ) };
    // all started asynchronously in the background

    var handles = procs.Select( p => p.Handle ).ToArray();
    WaitForMultipleObjects( (uint)handles.Length, handles, true, uint.MaxValue ); // uint.maxvalue waits forever

}
于 2008-10-09T00:12:28.610 に答える
3

参考までに: IDisposableオブジェクトのusingキーワード:

using(Writer writer = new Writer())
{
    writer.Write("Hello");
}

は単なるコンパイラ構文です。コンパイルすると次のようになります。

Writer writer = null;
try
{
    writer = new Writer();
    writer.Write("Hello");
}
finally
{
    if( writer != null)
    {
        ((IDisposable)writer).Dispose();
    }
}

usingコンパイラは、using ブロック内でライター参照を再割り当てできないようにするため、少し優れています。

フレームワークのガイドライン セクション 9.3.1 p. 256 状態:

close がこの分野の標準的な用語である場合は、Dispose() に加えてメソッド Close() を提供することを検討してください。


コード例では、外側の try-catch は不要です (上記を参照)。

pDispose()は範囲外になるとすぐに呼び出されるため、使用はおそらくここでやりたいことではありません。これはプロセスをシャットダウンしません (テスト済み)。

プロセスは独立しているため、呼び出さない限り、プロセスはp.WaitForExit()スピンオフし、プログラムから完全に独立して独自のことを行います。

直観に反して、Process の場合、Close() はリソースを解放するだけで、プログラムは実行したままにします。CloseMainWindow() は一部のプロセスで機能し、Kill() は任意のプロセスを強制終了します。CloseMainWindow() と Kill() はどちらも例外をスローする可能性があるため、finally ブロックで使用する場合は注意してください。

最後に、プロセスの終了を待機するが、例外が発生してもプロセスを強制終了しないコードをいくつか示します。オリオン・エドワーズより優れていると言っているのではなく、ただ違うだけです。

List<System.Diagnostics.Process> processList = new List<System.Diagnostics.Process>();

try
{
    foreach (string command in Commands)
    {
        processList.Add(System.Diagnostics.Process.Start(command));
    }

    // loop until all spawned processes Exit normally.
    while (processList.Any())
    {
        System.Threading.Thread.Sleep(1000); // wait and see.
        List<System.Diagnostics.Process> finished = (from o in processList
                                                     where o.HasExited
                                                     select o).ToList();

        processList = processList.Except(finished).ToList();
        foreach (var p in finished)
        {
            // could inspect exit code and exit time.
            // note many properties are unavailable after process exits
            p.Close();
        }
    }
}
catch (Exception ex)
{
    // log the exception
    throw;
}
finally
{
    foreach (var p in processList)
    {
        if (p != null)
        {
            //if (!p.HasExited)
            // processes will still be running
            // but CloseMainWindow() or Kill() can throw exceptions
            p.Dispose();
        }

    }
}

コードがさらに醜くなり始めるので、プロセスを強制終了することは気にしませんでした。詳細については、msdnのドキュメントを参照してください。

于 2008-10-09T01:50:13.750 に答える
0
try
{
   foreach(string command in S) // command is something like "c:\a.exe"
   {
      using(p = Process.Start(command))
      {
        // I literally put nothing in here.
      }

    } 
}
catch (Exception e)
{
    // notify of process failure
}

それが機能する理由は、例外が発生すると、変数 p がスコープ外になり、プロセスを閉じる Dispose メソッドが呼び出されるためです。さらに、実行可能ファイルが終了するのを待ってから次のコマンドに進むのではなく、コマンドごとにスレッドをスピンオフすることをお勧めします。

于 2008-10-08T23:58:49.030 に答える