首页 > 解决方案 > 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()不会返回,尽管进程重新启动,这意味着它被卡住了。挖掘后我发现了一些东西,但仍然无法通过。

  1. 它挂在进程中的锁上:

此方法来自 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;
        }
      }
    }
  1. 使用Dispatcher.InvokeAsync()而不是Invoke()可以解决这个问题。

C# 中的lock 块是 Monitor.Enter() 的缩写,它是可重入的。并且 Invoke() 保证它与运行到锁的线程相同。

我很好奇为什么会卡住?

标签: c#wpfmultithreading

解决方案


尝试使用这个:

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方法。这确保了进程在重新启动之前被完全杀死。


推荐阅读