我想等待一个进程完成,但是process.WaitForExit()挂起了我的图形用户界面。有没有一种基于事件的方法,或者我需要派生一个线程来阻塞,直到退出,然后自己委托事件?
发布于 2009-01-22 18:23:21
process.EnableRaisingEvents =真;
process.Exited += EventHandler
发布于 2013-10-01 05:40:36
从.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))
{
if (process.HasExited) return Task.CompletedTask;
var tcs = new TaskCompletionSource<object>();
process.EnableRaisingEvents = true;
process.Exited += (sender, args) => tcs.TrySetResult(null);
if(cancellationToken != default(CancellationToken))
cancellationToken.Register(() => tcs.SetCanceled());
return process.HasExited ? Task.CompletedTask : tcs.Task;
}用法:
public async void Test()
{
var process = new Process("processName");
process.Start();
await process.WaitForExitAsync();
//Do some fun stuff here...
}发布于 2018-05-22 15:01:55
下面是一个稍微干净一些的扩展方法,因为它清理了取消令牌注册和退出事件。它还处理争用条件边缘情况,在这种情况下,进程可以在它启动之后但在附加Exited事件之前结束。它使用C# 7中新的本地函数语法。
public static class ProcessExtensions
{
public static async Task WaitForExitAsync(this Process process, CancellationToken cancellationToken = default)
{
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
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.ConfigureAwait(false);
}
}
finally
{
process.Exited -= Process_Exited;
}
}
}编辑:
我已经向TaskCompletionSource构造函数添加了TaskCreationOptions.RunContinuationsAsynchronously,它可以修复当TrySetResult()试图同步运行延续时可能发生的死锁。
这种死锁情况实际上很难追踪到。事实证明,TaskCompletionSource.SetResult()在默认情况下同步运行continuations,这将导致await下的所有代码都在SetResult()中运行。这通常不是问题,因为Process.Exited是在线程池线程上运行的。但是,整个Process.Exited回调都是在新线程池线程内的this锁中运行的,其中this是Process实例。这可以在here中看到。
由于C#语言规范实现事件处理程序的方式,process.Exited -= Process_Exited;也对此进行了锁定。最终结果是两个单独的线程池线程最终阻塞在Process实例上的一个锁上。太疯狂了!更疯狂的是,如果您没有同步上下文,紧接在await WaitForExitAsync()下面的代码也可能同步运行,因此您最终将在Process.Exited回调中的lock中运行大量代码。如果你不知道这种行为,TaskCompletionSource是非常危险的!
解决方案是向TaskCompletionSource构造函数添加TaskCreationOptions.RunContinuationsAsynchronously。这允许TaskCompletionSource.SetResult()立即返回,一切都将“按预期”工作。
https://stackoverflow.com/questions/470256
复制相似问题