【问题标题】:Restart Process Hangs in Exited Handler by Dispatcher.Invoke()Dispatcher.Invoke() 在退出的处理程序中重新启动进程挂起
【发布时间】:2020-07-02 06:36:50
【问题描述】:

这是一个基于 .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中的Start()。进程重启后Invoke没有返回,这意味着卡住了。 挖掘后发现了一些东西,但仍然无法通过。

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

此方法来自donet框架源码。它是通过多个以 Process.Start() 为根的调用来调用的。

The whole file can be found here

 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() 可以解决挂起问题。

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

我很好奇为什么会卡住?

【问题讨论】:

  • lock(this) 是不好的做法,this 引用可以在外部更改
  • 这能回答你的问题吗? Best Practices in using a lock
  • @PavelAnikhouski 可能不是。 EnsureWatchingForExit 代码段不是我编写的。来自donet框架源代码。
  • 我敢冒险重用现有的Process 实例可能是个坏主意。如果您丢弃旧的 Process 实例并启动一个新实例而不是重复使用会发生什么?
  • @Mufaka 还是不行:(

标签: c# wpf multithreading


【解决方案1】:

试试这个:

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 { }
            }

编辑

根据 cmets,如果要在正确终止后调用 Start,则应在进程对象中调用 WaitForExit 方法。这样可以确保进程在重新启动之前被完全杀死。

【讨论】:

  • 它似乎重新启动了当前进程。但我需要重新启动其他进程。
  • 要重启其他进程使用processObject.Kill()然后Process.Start(patheToProcess);
  • 是的,确实如此。但它会像描述的那样卡住。为什么在 OnExited 处理程序中调用 Start() 而不仅仅是在 Kill() 语句之后的原因是我需要在前一个进程终止后严格调用 Start()。 Kill() 可能在进程实际终止之前返回。
  • 您需要在流程对象上调用WaitForExit,就像在答案的编辑中一样。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多