c# 备份oracle waitforexit()方法死锁,关于c#:process.WaitForExit()异步

我想等待进程完成,但是process.WaitForExit()挂起了我的GUI。 有没有一种基于事件的方式,还是我需要生成一个线程来阻塞直到退出,然后自己委托该事件?

这是Process的完全异步实现,使您还可以重定向标准输出和标准错误流stackoverflow.com/a/39872058/1212017。

从.NET 4.0 / C#5开始,最好使用异步模式来表示它。

///

/// Waits asynchronously for the process to exit.

///

/// The process to wait for cancellation.

/// A cancellation token. If invoked, the task will return

/// immediately as canceled.

/// A Task representing waiting for the process to end.

public static Task WaitForExitAsync(this Process process,

CancellationToken cancellationToken = default(CancellationToken))

{

var tcs = new TaskCompletionSource();

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...

}

谢谢,这很有帮助。但是,我遇到的问题是,如果任务已取消,tcs.SetResult(null)会引发InvalidOperationException,如果取消任务后进程退出,则可能会发生这种情况。为了解决这个问题,我用tcs.TrySetResult替换了tcs.SetResult。

@aj_r小提示。谢谢。我做了更新。

如果进程在注册退出的处理程序之前停止,那么我们将永远等待。必须在Start之前完成注册,因此编写StartAsync方法更加容易。大部分是相同的代码,但在返回行之前恰好使用process.Start()命名为StartAsync。

的确如此-该方法可以检查进程是否已经退出,如果已经退出,则立即返回。但这可能会给人一种错误的安全感,因为它不是线程/进程安全的。如果可能发生意外退出,则不需要单独的方法,只需在启动进程之前调用此方法,并保留任务以备稍后使用。

请注意,这里的取消代码不会杀死该进程,因此,如果挂起该进程,它将保持运行状态。如果要在取消时停止该进程,请更改为以下内容:cancelleToken.Register(()=> {process.Kill(); tcs.SetCanceled();});

为什么不只是await Task.Run(() => process.WaitForExit())而不是所有这些事件呢?

@LonelyPixel因为它将在进程运行的所有时间都阻塞线程?

@TN好吧,不,这就是await的作用,它已经在答案的代码中。我的短很多,做同样的事情(我相信)。

@LonelyPixel IMO,WaitForExit()将在进程运行期间阻止线程。取决于TaskScheduler,通常不是调用的那个,而是ThreadPool的一个线程。答案中的解决方案可能不会阻止ThreadPool线程。 (对您来说可能不是问题。)

每当有人键入await Task.Run时,一只小狗就会死亡。

为了避免死锁并避免编写StartAsync函数,可以在" process.Exited + ="之后添加一个代码" if(process.HasExited)tcs.TrySetResult(null);"。

好的解决方案。我需要该过程的退出代码,因此我将返回类型从Task更改为Task,将TrySetResult参数从null更改为process.ExitCode。

值得阅读@Dmitriy Nemykin的答案,对我有很大帮助

死锁警告!非Windows编码器,看看我的答案!

第一条评论的对立面也成立:如果任务完成,然后触发取消源,它也会给出InvalidOperationException。应该是.Register(() => tcs.TrySetCanceled())。

@MgSam如何使用异步实现添加超时?目前,我将Process.WaitForExit(Int32)与TimeOut一起使用,并希望将其转换为Async但保持超时。

异步方法的最后一行应该是:return process.HasExited? Task.CompletedTask:tcs.Task;如果在我们收听事件之前该过程已经退出。

process.EnableRaisingEvents = true;

process.Exited + = [EventHandler]

这是一种更简洁的扩展方法,因为它清除了取消令牌注册和Exited事件。它还处理竞争条件边缘情况,该过程可以在开始后但在附加Exited事件之前结束。它使用C#7中的新本地函数语法。

public static class ProcessExtensions

{

public static async Task WaitForExitAsync(this Process process, CancellationToken cancellationToken = default)

{

var tcs = new TaskCompletionSource();

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;

}

}

}

死锁警告!非Windows编码器,看看我的答案!

如果选择@MgSam答案,请注意,如果您通过WaitForExitAsync某些CancellationToken,则在指定的延迟后将自动取消,您可以获得InvalidOperationException。要解决此问题,您需要进行更改

cancellationToken.Register(tcs.SetCanceled);

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

附言:别忘了及时处置CancellationTokenSource。

那个PS救了我....

根据此链接,使用WaitForExit()方法使当前线程等待,直到关联的进程终止。但是,该流程确实有一个Exited事件可以挂接到。

那是什么msdn.microsoft.com/zh-CN/library/

不正确,应该删除答案,以免引起我的猜疑。

我猜想以前的评论者曾在此答案中想象过"不"一词吗?没有一个。

Ryan解决方案在Windows上效果很好。在OSX上发生了奇怪的事情,它可能是tcs.TrySetResult()的死锁!有两种解决方案:

第一:

将tcs.TrySetResult()包装到Task.Run():

public static async Task WaitForExitAsync(this Process process, CancellationToken cancellationToken = default)

{

var tcs = new TaskCompletionSource();

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毫秒增加到更多,具体取决于您的应用程序。

使用System.Diagnostics.Process.Exited

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,针对你的问题,以下是关于 C# 输入输出重定向的方法一:同步 Process 的解答。 在 C# 中,可以使用 Process 类来执行外部程序,并且可以通过重定向输入和输出流来实现与外部程序的交互。具体步骤如下: 1. 创建 ProcessStartInfo 对象,设置 FileName 属性为要执行的外部程序的路径,设置 RedirectStandardInput 和 RedirectStandardOutput 属性为 true,表示重定向输入和输出流。 2. 创建 Process 对象,设置 StartInfo 属性为上述 ProcessStartInfo 对象,然后调用 Start 方法启动进程。 3. 使用 Process 对象的 StandardInput 属性获取重定向的输入流,使用 WriteLine 方法向外部程序写入数据。 4. 使用 Process 对象的 StandardOutput 属性获取重定向的输出流,使用 ReadLine 方法从外部程序读取数据。 以下是一个简单的示例代码: ``` using System; using System.Diagnostics; class Program { static void Main() { var startInfo = new ProcessStartInfo { FileName = "外部程序路径", RedirectStandardInput = true, RedirectStandardOutput = true, UseShellExecute = false }; var process = new Process { StartInfo = startInfo }; process.Start(); var standardInput = process.StandardInput; var standardOutput = process.StandardOutput; standardInput.WriteLine("要写入的数据"); var output = standardOutput.ReadLine(); Console.WriteLine(output); process.WaitForExit(); } } ``` 以上就是 C# 输入输出重定向方法一:同步 Process 的解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值