Я хочу дождаться завершения процесса, но process.WaitForExit()
зависает мой графический интерфейс. Есть ли способ, основанный на событиях, или мне нужно создать поток для блокировки до выхода, а затем делегировать событие самостоятельно?
Process.WaitForExit() асинхронно
Ответ 1
process.EnableRaisingEvents= true;
process.Exited + = [EventHandler]
Ответ 2
Как и для .NET 4.0/С# 5, лучше представить это с использованием шаблона async.
/// <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...
}
Ответ 3
Здесь метод расширения, который немного чище, потому что он очищает регистрацию токена отмены и событие Exited. Он также обрабатывает крайний случай состояния гонки, где процесс может завершиться после того, как он начался, но до присоединения события Exited. Он использует новый синтаксис локальных функций в С# 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;
}
}
}
Ответ 4
Если вы выберете ответ @MgSam, имейте в виду, что если вы пройдете через WaitForExitAsync
some CancellationToken
, который будет автоматически отменен после указанной задержки, вы можете получить InvalidOperationException
. Чтобы исправить это, вам нужно изменить
cancellationToken.Register(tcs.SetCanceled);
к
cancellationToken.Register( () => { tcs.TrySetCanceled(); } );
P.S.: не забывайте своевременно распоряжаться своим CancellationTokenSource
.
Ответ 5
В соответствии с этой ссылкой метод WaitForExit() используется для того, чтобы текущий поток ожидал, пока соответствующий процесс не завершится. Тем не менее, процесс имеет событие "Выход", в которое вы можете подключиться.
Ответ 6
Используйте System.Diagnostics.Process.Exited
Ответ 7
Решение Райана хорошо работает на окнах. На OSX происходили странные вещи, это может быть тупик в tcs.TrySetResult()
! Есть 2 решения:
Первый:
Оберните tcs.TrySetResult()
в Task.Run():
public static async Task WaitForExitAsync(this Process process, CancellationToken cancellationToken = default)
{
var tcs = new TaskCompletionSource<bool>();
void Process_Exited(object sender, EventArgs e)
{
Task.Run(() => tcs.TrySetResult(true));
}
process.EnableRaisingEvents = true;
process.Exited += Process_Exited;
try
{
if (process.HasExited)
{
return;
}
using (cancellationToken.Register(() => Task.Run(() => tcs.TrySetCanceled())))
{
await tcs.Task;
}
}
finally
{
process.Exited -= Process_Exited;
}
}
Разговор об этом и других деталях: Вызов TaskCompletionSource.SetResult неблокирующим образом.
Второй:
public static async Task WaitForExitAsync(this Process process, CancellationToken cancellationToken)
{
while (!process.HasExited)
{
await Task.Delay(100, cancellationToken);
}
}
Вы можете увеличить интервал опроса от 100 мс до более в зависимости от вашего приложения.