c# - Dispatcher.Invoke() 在退出的处理程序中重新启动进程挂起
问题描述
这是一个基于 .net 4.7.2 的 WPF 项目。
为了重新启动一个进程(不是主进程本身,而是由主进程启动的其他进程),在进程的退出事件处理程序中调用 process.Start()。
简化代码如下:
public MainWindow()
{
InitializeComponent();
Process process = new Process()
{
StartInfo =
{
FileName = *<path to a bat file>*,
WorkingDirectory = *<folder to the bat file>*,
UseShellExecute = true,
},
EnableRaisingEvents = true,
};
// register handler
process.Exited += (sender, args) =>
{
Dispatcher.Invoke(() =>
{
process.Start(); // restart after be killed
});
Console.Out.WriteLine("OnExit Finish");
};
// start for the first time
process.Start();
// wait and kill
Thread.sleep(2000);
process.CloseMainWindow();
process.Kill();
}
问题是Dispatcher.Invoke 中的Start()不会返回,尽管进程重新启动,这意味着它被卡住了。挖掘后我发现了一些东西,但仍然无法通过。
- 它挂在进程中的锁上:
此方法来自 donet 框架源代码。它是通过多个以 Process.Start() 为根的调用来调用的。
private void EnsureWatchingForExit()
{
if (this.watchingForExit)
return;
lock (this) // ***** IT HANGS *****
{
if (this.watchingForExit)
return;
this.watchingForExit = true;
try
{
this.waitHandle = (WaitHandle) new ProcessWaitHandle(this.m_processHandle);
this.registeredWaitHandle = ThreadPool.RegisterWaitForSingleObject(this.waitHandle, new WaitOrTimerCallback(this.CompletionCallback), (object) null, -1, true);
}
catch
{
this.watchingForExit = false;
throw;
}
}
}
- 使用Dispatcher.InvokeAsync()而不是Invoke()可以解决这个问题。
C# 中的lock 块是 Monitor.Enter() 的缩写,它是可重入的。并且 Invoke() 保证它与运行到锁的线程相同。
我很好奇为什么会卡住?
解决方案
尝试使用这个:
public static void Restart()
{
try
{
var m = (Application.Current.MainWindow as Window);
if (m != null)
{
m.Visibility = Visibility.Collapsed;
m.Close();
}
Process.Start(Application.ResourceAssembly.Location);
Application.Current.Shutdown();
}
catch { }
}
编辑
根据评论,如果您想Start
在正确终止后被调用,您应该在进程对象中调用WaitForExit方法。这确保了进程在重新启动之前被完全杀死。
推荐阅读
- python - PCA 无法在散点图上获得颜色
- angular - 如何在不刷新窗口的情况下从后端重新加载数据?
- angular - Firestore 安全规则 - 只允许 Story 成员读取
- org-mode - 将 orgmode doc 导出到 freemind 时如何指定无限数量的级别?
- java - 如何检查Array中的两个char值是否相同?
- javascript - 如何从咖啡脚本中使用 vue?
- python - 使用列表理解将字符串拆分为 K 个子字符串
- c++ - C++ 从受保护的抽象/派生类创建变量
- php - 在 laravel 中获取 REFERRER
- c - 为什么 gcc 使用 -I 选项找不到包含路径?