процесс.WaitForExit() асинхронно

Я хочу дождаться завершения процесса, но process.WaitForExit() зависает мой GUI. Есть ли способ на основе событий или мне нужно создать поток для блокировки до выхода, а затем делегировать событие самому?

6 ответов



начиная с .NET 4.0 / C# 5, лучше представить это с помощью асинхронного шаблона.

/// <summary>
/// Waits asynchronously for the process to exit.
/// </summary>
/// <param name="process">The process to wait for cancellation.</param>
/// <param name="cancellationToken">A cancellation token. If invoked, the task will return 
/// immediately as canceled.</param>
/// <returns>A Task representing waiting for the process to end.</returns>
public static Task WaitForExitAsync(this Process process, 
    CancellationToken cancellationToken = default(CancellationToken))
{
    var tcs = new TaskCompletionSource<object>();
    process.EnableRaisingEvents = true;
    process.Exited += (sender, args) => tcs.TrySetResult(null);
    if(cancellationToken != default(CancellationToken))
        cancellationToken.Register(tcs.SetCanceled);

    return tcs.Task;
}

использование:

public async void Test() 
{
   var process = new Process("processName");
   process.Start();
   await process.WaitForExitAsync();

   //Do some fun stuff here...
}

если вы выбираете @MgSam ответ, имейте в виду, если вы проходите через WaitForExitAsync некоторые CancellationToken, который будет автоматически отменен после указанной задержки, вы можете получить InvalidOperationException. Чтобы исправить это, вам нужно изменить

cancellationToken.Register(tcs.SetCanceled);

to

cancellationToken.Register( () => { tcs.TrySetCanceled(); } );

P. S.: Не забудьте выбросить CancellationTokenSource в время.


вот метод расширения, который немного чище, потому что он очищает регистрацию токена отмены и выходное событие. Он также обрабатывает крайний случай состояния гонки, где процесс может закончиться после его запуска, но до того, как выходное событие было присоединено. Он использует новый синтаксис локальных функций в C# 7.

public static class ProcessExtensions
{
    public static async Task WaitForExitAsync(this Process process, CancellationToken cancellationToken = default)
    {
        var tcs = new TaskCompletionSource<bool>();

        void Process_Exited(object sender, EventArgs e)
        {
             tcs.TrySetResult(true);
        }

        process.EnableRaisingEvents = true;
        process.Exited += Process_Exited;

        try
        {
            if (process.HasExited)
            {
                return;
            }

            using (cancellationToken.Register(() => tcs.TrySetCanceled()))
            {
                await tcs.Task;
            }
        }
        finally
        {
            process.Exited -= Process_Exited;
        }
    }
}

по этой ссылке метод WaitForExit () используется для того, чтобы заставить текущий поток ждать завершения связанного процесса. Однако у процесса есть выходное событие, которое вы можете подключить.


использовать System.Diagnostics.Process.Exited