BeginInvoke is blocking the UI, whereas Invoke is not. Why?
Asked Answered
N

1

6

I am confused with scenario which I have encountered with cross thread access. Here is what I am trying to do:

Main UI thread - menu item click I create a background worker and run it asynchronously

private void actionSubMenuItem_Click(object sender, EventArgs e)
{
       ToolStripMenuItem itemSelected = (ToolStripMenuItem)sender;
       ExecuteTheActionSelected(itemSelected.Text);
}

The method ExecuteTheActionSelected is as follows:

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

The localBackgroundWorker_DoWork has:

 ActionExecutionHelper actionExecutioner = new ActionExecutionHelper()
 actionExecutioner.Execute();

The Execute method in that class that has method invoker which infact invokes the event handler in UI thread:

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

The UI event handler is as follows:

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

Now here comes the cross thread issue:

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

Here Invoke doesn't block the UI where as BeginInvoke blocks. Please help me understand this scenario as i m confused a lot.

Nikolas answered 8/5, 2012 at 11:26 Comment(1)
I'm afraid I don't know the answer, but the approach I would take to finding it out would be looking at how the multiple levels of invocation and background workers interact with each other, and where everything actually gets executed so that you understand how everything fits together.Corinacorine
M
7

Yes, this is normal. The benefit you get out of Invoke() is that it blocks the worker thread. When you use BeginInvoke() the thread keeps motoring and issues invoke requests at a rate higher than the UI thread can handle. It depends on what you ask the UI thread to do but it starts to become a problem around 1000 invokes per second.

The UI thread stops being responsive in this scenario, it is constantly finding another invoke request back while it pumps the message loop and doesn't get around doing its regular duties anymore. Input and paint requests no longer get processed.

The clear source of the problem is the invoke request on every single line of output retrieved from the process. It is just generating them too quickly. You need to fix this by lowering the rate at which you invoke. There's a simple rule for that, you are only trying to keep a human occupied, invoking more than 25 times per second turns whatever you produce in but a blur to the eye. So buffer the lines and measure the amount of time that has passed since the last invoke call.

Also note that using Invoke() is an easy workaround but it isn't exactly guaranteed to work. It is a race, the worker thread could potentially always call the next Invoke() a wee bit earlier than the main thread re-entering the message loop and reading the next message. In which case you will still have the exact same problem.

Monastic answered 8/5, 2012 at 12:22 Comment(3)
Okay that gives explanation about BeginInvoke() blocking the UI. But why does the Invoke() not block the UI thread? I can access other controls easily when I use Invoke()Nikolas
Because it blocks the worker thread so it cannot slam the UI thread with invoke requests so often. Key point is that the UI thread isn't actually blocked, it is just not taking care of all of its normal duties.Monastic
One more question !! How could know about such minor things ?? :) Man that one requires more of depth knowledge with what you explore. I searched many sites, but could never come to such conclusion !Nikolas

© 2022 - 2024 — McMap. All rights reserved.