web-dev-qa-db-ja.com

非同期プロセスの開始と終了の待機

.netのスレッドモデルは初めてです。何に使用しますか:

  1. ファイルを処理するプロセスを開始します(process.StartInfo.FileName = fileName;)
  2. ユーザーがプロセスを閉じるのを待つORしばらくしてスレッドを放棄する
  3. ユーザーがプロセスを閉じた場合、ファイルを削除します

この操作はアプリケーションに影響を与えないため、プロセスの開始と待機はメインスレッドとは異なるスレッドで実行する必要があります。

例:

私のアプリケーションはhtmlレポートを作成します。ユーザーはどこかを右クリックして「レポートを表示」と言うことができます-今、レポートの内容を一時ファイルで取得し、htmlファイルを処理するプロセス、つまりデフォルトのブラウザーを起動します。問題は、クリーンアップ、つまり一時ファイルを削除できないことです。

36

「そして、待つことは非同期でなければならない」-私はおかしくなろうとはしていませんが、それは用語の矛盾ではありませんか?ただし、Processを開始しているので、Exitedイベントが役立ちます。

ProcessStartInfo startInfo = null;
Process process = Process.Start(startInfo);
process.EnableRaisingEvents = true;
process.Exited += delegate {/* clean up*/};

実際に待機する場合(タイムアウトなど)、次のようにします。

if(process.WaitForExit(timeout)) {
    // user exited
} else {
    // timeout (perhaps process.Kill();)
} 

非同期を待機するには、おそらく別のスレッドを使用しますか?

ThreadPool.QueueUserWorkItem(delegate {
    Process process = Process.Start(startInfo);
    if(process.WaitForExit(timeout)) {
        // user exited
    } else {
        // timeout
    }
});
61
Marc Gravell

この古い質問に対する高度な代替案を追加します。スレッドをブロックせずにプロセスが終了するのを待ち、それでもタイムアウトをサポートしたい場合は、次を試してください:

    public static Task<bool> WaitForExitAsync(this Process process, TimeSpan timeout)
    {
        ManualResetEvent processWaitObject = new ManualResetEvent(false);
        processWaitObject.SafeWaitHandle = new SafeWaitHandle(process.Handle, false);

        TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();

        RegisteredWaitHandle registeredProcessWaitHandle = null;
        registeredProcessWaitHandle = ThreadPool.RegisterWaitForSingleObject(
            processWaitObject,
            delegate(object state, bool timedOut)
            {
                if (!timedOut)
                {
                    registeredProcessWaitHandle.Unregister(null);
                }

                processWaitObject.Dispose();
                tcs.SetResult(!timedOut);
            },
            null /* state */,
            timeout,
            true /* executeOnlyOnce */);

        return tcs.Task;
    }

繰り返しますが、受け入れられた答えと比較したこのアプローチの利点は、スレッドをブロックしないことです。これにより、アプリのオーバーヘッドが削減されます。

18
Chris Gillum

次のコードを試してください。

public void KickOffProcess(string filePath) {
  var proc = Process.Start(filePath);
  ThreadPool.QueueUserWorkItem(new WaitCallBack(WaitForProc), proc);
}

private void WaitForProc(object obj) {
  var proc = (Process)obj;
  proc.WaitForExit();
  // Do the file deletion here
}
4
JaredPar

ファイルを開くために別のプロセスを使用することはおそらくないでしょう。代わりに、おそらくバックグラウンドスレッドを使用します(操作に時間がかかり、UIスレッドがブロックされる可能性があると考えた場合)。

private delegate void FileOpenDelegate(string filename);

public void OpenFile(string filename)
{
   FileOpenDelegate fileOpenDelegate = OpenFileAsync;
   AsyncCallback callback = AsyncCompleteMethod;
   fileOpenDelegate.BeginInvoke(filename, callback, state);
}

private void OpenFileAsync(string filename)
{
   // file opening code here, and then do whatever with the file
}

もちろん、これは良い動作例ではなく(何も返さない)、UIの更新方法は示していません(バックグラウンドスレッドではUIスレッドを更新できないため、UIレベルでBeginInvokeを使用する必要があります)。しかし、このアプローチは、一般的に.Netで非同期操作を処理する方法です。

0
Chris Holmes

ProcessクラスでExitedイベントを使用できます

ProcessStartInfo info = new ProcessStartInfo();

info.FileName = "notepad.exe";
Process process = Process.Start(info);

process.Exited += new EventHandler(process_Exited);
Console.Read();

そのイベントでは、あなたが言及した操作を処理できます

0
Pablo Retyk