首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >异步process.WaitForExit()

异步process.WaitForExit()
EN

Stack Overflow用户
提问于 2009-01-22 18:13:58
回答 9查看 40.4K关注 0票数 68

我想等待一个进程完成,但是process.WaitForExit()挂起了我的图形用户界面。有没有一种基于事件的方法,或者我需要派生一个线程来阻塞,直到退出,然后自己委托事件?

EN

回答 9

Stack Overflow用户

回答已采纳

发布于 2009-01-22 18:23:21

process.EnableRaisingEvents =真;

process.Exited += EventHandler

票数 34
EN

Stack Overflow用户

发布于 2013-10-01 05:40:36

从.NET 4.0/C# 5开始,使用异步模式来表示这一点更好。

代码语言:javascript
复制
/// <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;
}

用法:

代码语言:javascript
复制
public async void Test() 
{
   var process = new Process("processName");
   process.Start();
   await process.WaitForExitAsync();

   //Do some fun stuff here...
}
票数 121
EN

Stack Overflow用户

发布于 2018-05-22 15:01:55

下面是一个稍微干净一些的扩展方法,因为它清理了取消令牌注册和退出事件。它还处理争用条件边缘情况,在这种情况下,进程可以在它启动之后但在附加Exited事件之前结束。它使用C# 7中新的本地函数语法。

代码语言:javascript
复制
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锁中运行的,其中thisProcess实例。这可以在here中看到。

由于C#语言规范实现事件处理程序的方式,process.Exited -= Process_Exited;也对此进行了锁定。最终结果是两个单独的线程池线程最终阻塞在Process实例上的一个锁上。太疯狂了!更疯狂的是,如果您没有同步上下文,紧接在await WaitForExitAsync()下面的代码也可能同步运行,因此您最终将在Process.Exited回调中的lock中运行大量代码。如果你不知道这种行为,TaskCompletionSource是非常危险的!

解决方案是向TaskCompletionSource构造函数添加TaskCreationOptions.RunContinuationsAsynchronously。这允许TaskCompletionSource.SetResult()立即返回,一切都将“按预期”工作。

票数 24
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/470256

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档