BeginInvoke阻止了UI,而Invoke却没有.

Nag*_*tri 6 c# multithreading begininvoke invokerequired winforms

我对跨线程访问遇到的场景感到困惑.这是我想要做的:

主UI线程 - 菜单项单击我创建后台工作程序并异步运行它

private void actionSubMenuItem_Click(object sender, EventArgs e)
{
       ToolStripMenuItem itemSelected = (ToolStripMenuItem)sender;
       ExecuteTheActionSelected(itemSelected.Text);
}
Run Code Online (Sandbox Code Playgroud)

方法ExecuteTheActionSelected如下:

private void ExecuteTheActionSelected(string actionSelected)
{
      BackgroundWorker localBackgroundWorker = new BackgroundWorker();
      localBackgroundWorker.DoWork += new DoWorkEventHandler(localBackgroundWorker_DoWork);
      localBackgroundWorker.RunWorkerAsync(SynchronizationContext.Current);
}
Run Code Online (Sandbox Code Playgroud)

localBackgroundWorker_DoWork有:

 ActionExecutionHelper actionExecutioner = new ActionExecutionHelper()
 actionExecutioner.Execute();
Run Code Online (Sandbox Code Playgroud)

Execute在于类具有INFACT调用在UI线程的事件处理方法调用方法:

 public void Execute()
 {
      // ---- CODE -----
      new MethodInvoker(ReadStdOut).BeginInvoke(null, null);
 }

 protected virtual void ReadStdOut()
 {
      string str;
      while ((str = executionProcess.StandardOutput.ReadLine()) != null)
      {
          object sender = new object();
          DataReceivedEventArgs e = new DataReceivedEventArgs(str);
          outputDataReceived.Invoke(sender, e); 
          //This delegate invokes UI event handler
      }
 }
Run Code Online (Sandbox Code Playgroud)

UI事件处理程序如下:

private void executionProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
    if (_dwExecuteAction != null)
    {
        _dwExecuteAction.ShowDataInExecutionWindow(e.Text);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在出现了跨线程问题:

public void ShowDataInExecutionWindow(string message)
{
     if (rchtxtExecutionResults.InvokeRequired)
     {
            rchtxtExecutionResults.Invoke(new ShowDataExecutionDelegate(ShowDataInExecutionWindow), message);
     }
     else
     {
            this.rchtxtExecutionResults.AppendText(message + Environment.NewLine);
     }
}
Run Code Online (Sandbox Code Playgroud)

这里Invoke不会阻止作为BeginInvoke阻塞的UI.请帮助我理解这个场景,因为我很困惑.

Han*_*ant 7

是的,这很正常.从Invoke()中获得的好处是它会阻塞工作线程.当您使用BeginInvoke()时,线程会保持驱动并以高于UI线程可以处理的速率发出调用请求.这取决于你要求UI线程做什么,但它开始成为每秒1000次调用的问题.

在这种情况下,UI线程停止响应,它不断发现另一个调用请求,当它泵出消息循环并且不再执行其常规任务时.输入和绘制请求不再被处理.

问题的明确来源是从进程检索的每一行输出上的调用请求.它只是过快地产生它们.您需要通过降低调用率来解决此问题.对此有一个简单的规则,你只是试图保持一个人的占用,每秒调用超过25次,无论你产生什么,但眼睛模糊.因此缓冲行并测量自上次调用调用以来经过的时间量.

另请注意,使用Invoke()是一种简单的解决方法,但并不能保证能够正常工作.这是一场竞赛,工作线程可能总是比主线程重新进入消息循环并读取下一条消息更早地调用下一个Invoke().在这种情况下,您仍然会遇到完全相同的问题.