Dus*_*etz 43 .net c# user-interface asynchronous winforms
我想等待一个进程完成,但process.WaitForExit()挂起我的GUI.是否有基于事件的方式,或者我是否需要生成一个线程来阻止直到退出,然后自己委托事件?
MgS*_*Sam 86
从.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;
}
Run Code Online (Sandbox Code Playgroud)
用法:
public async void Test()
{
var process = new Process("processName");
process.Start();
await process.WaitForExitAsync();
//Do some fun stuff here...
}
Run Code Online (Sandbox Code Playgroud)
Rya*_*yan 13
这是一个稍微清晰的扩展方法,因为它清除了取消令牌注册和退出事件.它还处理竞争条件边缘情况,其中进程可以在它开始之后但在连接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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果你选择@MgSam答案,请注意,如果你通过了WaitForExitAsync一些CancellationToken,那将在指定的延迟后自动取消,你可以得到一个InvalidOperationException.要解决这个问题,你需要改变
cancellationToken.Register(tcs.SetCanceled);
Run Code Online (Sandbox Code Playgroud)
至
cancellationToken.Register( () => { tcs.TrySetCanceled(); } );
Run Code Online (Sandbox Code Playgroud)
PS:别忘了及时处理你CancellationTokenSource.
截至目前, Process提供了.NET5一个WaitForExitAsync。 class
await process.WaitForExitAsync( token );
Run Code Online (Sandbox Code Playgroud)
Ryan 解决方案在 Windows 上运行良好。在 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;
}
}
Run Code Online (Sandbox Code Playgroud)
有关此内容和更多详细信息的对话: 以非阻塞方式调用 TaskCompletionSource.SetResult
第二个:
public static async Task WaitForExitAsync(this Process process, CancellationToken cancellationToken)
{
while (!process.HasExited)
{
await Task.Delay(100, cancellationToken);
}
}
Run Code Online (Sandbox Code Playgroud)
您可以根据您的应用程序将轮询间隔从 100 毫秒增加到更长。
| 归档时间: |
|
| 查看次数: |
26610 次 |
| 最近记录: |