InvalidOperationException when Clicking a Tab and Calling Parallel.Invoke? - c#

Background
I am a person who is very new to doing threading/concurrent operations in C#, and am struggling with cross-threaded GUI applications. Right now I am experiencing an exception being thrown at run-time that I am not sure how to deal with. First, I will provide a background of what's going on, and then I will describe what I have tried to debug this.
I have a task that does a bunch of calculations.
readingDumpTask = new Task(() =>
{
this.myDumpEntries = BiteDump.GetEntriesFromFile(fileName);
});
When this task is completed, I want to do two things at once. I implement this process, in the following way...
// display dump information
displayDumpTask = readingDumpTask.ContinueWith(
delegate
{
Parallel.Invoke
(
() => this.DisplayEntries(),
() => this.DisplayDump()
);
}
, TaskScheduler.FromCurrentSynchronizationContext());
The two methods that are being delegated, work in the following way...
Method #1
private void DisplayEntries()
{
UpdateUIDelegate myDel = new UpdateUIDelegate(this.DisplayEntries);
if (this.InvokeRequired)
{
this.BeginInvoke(myDel);
}
// fills some data into labels on the GUI
}
Method #2
private void DisplayDump()
{
UpdateUIDelegate myDel = new UpdateUIDelegate(this.DisplayDump);
if (this.InvokeRequired)
{
this.BeginInvoke(myDel);
}
// fills some data into a RTB on the GUI
}
I would like to make it clear that none of the controls being used by method #1, are ever touched by method #2.
Problem Statement
The problem here, is that I am getting an InvalidOperationException as soon as I try to access the controls from inside of the DisplayDump function. I only ever get this exception when I have clicked on the Dump tab as shown below.
I think that this has something to do with the fact that the DisplayDump method is accessing the text-box sitting inside the Dump tab. Now, when I am on the Navigation tab, I never experience any problems and everything works safe/sound.
Attempted Debugging
I have only just this morning discovered how to even see the Parallel Tasks Debug Window let alone really know how to use it to solve my problems. So, I have removed one of the method calls from the Parallel.Invoke block and I see that it doesn't matter which tab I am on, I will never get the exception in this case (DisplayDump always works). However, as soon as I have both actions inside of the Parallel statement, the problem occurs. So... So far all I know is that it has something to do with the Parallel.Invoke.

After you call BeginInvoke, your method should return. If you fall through without a return, then the GUI will be accessed on a non-UI thread and you will get an InvalidOperationException.
private void DisplayEntries()
{
UpdateUIDelegate myDel = new UpdateUIDelegate(this.DisplayEntries);
if (this.InvokeRequired)
{
this.BeginInvoke(myDel);
return; // ** change here
}
// fills some data into labels on the GUI
}

Related

Window closing not working because of a race condition

Here's my code:
private void OpenLoadingWindow()
{
loadingWindow = new LoadingView();
loadingWindow.Closed += new EventHandler(LoadingWindow_Closed);
_go = true;
loadingWindow.ShowDialog();
}
public void OpenLoadingWindowInNewThread()
{
thread = new Thread(x => OpenLoadingWindow());
thread.IsBackground = true;
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
lock (_locker)
{
Monitor.Pulse(_locker);
}
}
public void CloseLoadingWindow()
{
lock (_locker)
while (!_go)
Monitor.Wait (_locker);
if (loadingWindow != null)
{
loadingWindow.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
{
_go = false;
loadingWindow.Close();
loadingWindow = null;
}));
}
}
In code I first call OpenLoadingWindowInNewThread() and after that I call CloseLoadingWindow(). However, the first time the code is executed it works fine. But after that, the code in CloseLoadingWindow(), in BeginInvoke doesnt get executed. What am I doing wrong?
What I want to achieve is this: open the loading window, execute some code. After the code is execited I call the closing method, I want to close the loading window.
The main problem here is that you are creating a second thread for UI. Don't do that.
Unfortunately, you didn't provide a good code example. So for the sake of an answer, let's assume you're doing something like this:
void button1_Click(object sender, EventArgs e)
{
DoLoadingWork();
}
void DoLoadingWork()
{
OpenLoadingWindowInNewThread();
LoadingWork();
CloseLoadingWindow();
}
I.e. some event happened in your UI, and now you have to do some work. You implemented this by calling the methods you've shown in your question, processing the work in your UI thread and creating a second thread to show the dialog.
This is the wrong way to approach this. Instead, you should keep all of your UI in the same thread, and do the work in a different thread. That would look more like this:
void DoLoadingWork()
{
using (LoadingView form = new LoadingView())
{
form.Shown += async (sender, e) =>
{
await Task.Run(() => LoadingWork());
form.Close();
};
form.ShowDialog();
}
}
This version does the following:
Creates your status dialog in the UI thread
Subscribes to the Shown event, to ensure the dialog is visible before anything else happens
Shows the dialog
Once the dialog is shown, a new thread is started to execute the LoadingWork() method
When the LoadingWork() method completes, the dialog is closed, allowing the dialog to be disposed and the DoLoadingWork() method to return.
Note that even if you have to interact with the UI from the code that does the processing, or if you need a way to interrupt the processing, the above is still the correct way to do things. Those other aspects of the requirements can easily be implemented, using standard idioms for dealing with them.
Without an actual example of what that processing might be, and how the UI interaction and/or interruption works, it's impossible to say exactly how that part would be implemented. But it would generally involve using Invoke() for UI interaction (or even better, refactoring the processing so that it uses async/await, with UI interaction occurring between await statements for the individual pieces of the work) and a flag or CancellationToken to deal with interrupting the thread.
If your processing does in fact interact with the UI, and you did in fact run it in the UI thread, then it's likely you've got calls to methods like Refresh() or Application.DoEvents() interspersed. These methods are practically never required, and IMHO are always a sign that the code has been implemented incorrectly. As an added benefit of changing your implementation to put the right code in the right thread, you won't have to use any of those methods to interact with the UI (instead, you'll use Invoke()).

Accessing the GUI from a background Thread

I've made a sample program which generates every second 2000 integers on a Background thread, and when it finishes it fires an event which draws graph on the GUI from the random generated data (I have a sleep inside my thread to simulate a real measurement).
private void SetChart(System.Windows.Forms.DataVisualization.Charting.Series series)
{
if (InvokeRequired)
{
SetChartCallback d = new SetChartCallback(SetChart);
this.Invoke(d, new object[] { series });
}
else
{
chart1.Series[0] = series;
chart1.Series[0].Name = "Generated Data";
}
}
I found this approach on the MSDN site. It's working fine, the only problem is, when I close the application. Sometimes an error meassage shows up :
Cannot access a disposed object.
Object name: 'Form1'.
When I close the program it disposes all the elements, how can I prevent this error not to happen?
You've closed the form, but the thread is still running, so when it completes It tries to invoke a method on the disposed object. Your form.
You can wait for the thread to complete.
Or you can signal it somehow to stop messing about creating integers you don't need anymore and quit it's loop right now.
Don't be tempted to just kill it. Very bad habit, you don't want get into that.
The proper approach, ugly as it may seem, is probably to catch the exception and swallow it. It's probably not reasonable for the form's Dispose to block until the background thread exits (a situation which could easily cause deadlock); nor does the Framework provide any method which says try to Invoke or BeginInvoke this method on a control or form, but simply do nothing if it's been disposed. Thus, your best bet is probably to write TryInvoke and TryBeginInvoke methods which will do that by catching any exception that results if the form has been disposed. You might use an IsDisposed check within such a method, but you should realize that because of some Framework quirks, there are some race conditions which cannot be resolved nicely.
A solution may be to check IsDisposed. Something like this:
private void SetChart(System.Windows.Forms.DataVisualization.Charting.Series series)
{
if (IsDisposed)
return;
// ...
}

How to block the flow, but keep the GUI responsive

I have a method which updates a richtextbox when it executes. I have to call this method a few times, without blocking the GUI. Once a call ended and the richtextbox populated, i have to block the flow to show some info to the user ( the next calls to the method should not start ). I have done this with a MessageBox.Show().
Here appears another problem, i have to be able to abort the program at any given time but the modal messagebox doesn't let me do that. Maybe you will tell me to get another form and simulate the messagebox, but hey, i still need to block the flow if the modal box will be replaced.
Yes, this is a multi-threading issue. There are many ways you can achieve this. Here is an explanation of what multi-threading is. What is multi-threading?
Also, here's my favorite tutorial on multi-threading: albahari.com
Threads have their own stack and operate independently. Here's one example of how to spawn a thread:
Thread _thread = new Thread(() => { MessageBox.Show("Hello world!", "Spawned in a different thread"); });
_thread.Start();
To invoke your richtextbox from another thread, one way to do it is to create a method inside of your form that appends text to your textbox by invoking it. Here is an example of how to do it:
public void AddText(string Text)
{
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(AddText); // Delegate
this.Invoke(d, new object[] { text });
}
else { this.textBox1.AppendText(text); }
}
Here is a msdn reference to what you're trying to do:
http://msdn.microsoft.com/en-us/library/ms171728.aspx

Winform: Multiple Threads Updating UI at Same Time

I inherited some code that has two non-UI threads that update various WinForm controls.
The code is using InvokeRequired and Invoke to update the UI; however, I still once in a while get the error: Cross-thread operation not valid: Control 'lvReports' accessed on a thread other than it was created on.
I suspect I am dealing with a race condition and that I need to introduce a lock into the method below, but that said, I can find dozens of examples on how to update UI from a non-UI thread safely but no examples or discussion on how to deal with two threads updating the same controls in a race scenario.
So my question is: how do I rewrite the code below to handle updating the UI properly given a race condition and that I need to update UI from non-UI threads?
// two separate theads call this method in a instance of a WinForm
private void LoadReports()
{
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(this.LoadReports));
}
else
{
// some code removed to keep exampe simple...
SetCtlVisible(lvReports, true);
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate { lvReports.Refresh(); });
}
else
{
lvReports.Refresh();
}
}
}
delegate void SetVisibleCallback(Control ctl, bool visible);
private void SetCtlVisible(Control ctl, bool visible)
{
if (ctl.InvokeRequired)
{
SetVisibleCallback d = new SetVisibleCallback(SetCtlVisible);
ctl.Invoke(d, new object[] { ctl, visible });
}
else
{
ctl.Visible = visible;
}
}
Here are some thoughts:
Does this.InvokeRequired differ from ctl.InvokeRequired at any time?
Is the second InvokeRequired test needed given the first?
Is the implementation of SetCtlVisible needed if I keep the first InvokeRequired?
Should I delete the first InvokeRequired and keep all the code in the else clause?
Is lock needed around the else clause?
Using InvokeRequired like this is an anti-pattern. You know that this method is getting called from a thread, InvokeRequired should always be true.
Now you can use it to troubleshoot your problem. If it is false then there's something seriously wrong. Throw an exception, the debugger will stop and let you find out why it isn't working properly. And always call Invoke(), invoke to a little helper method that does the rest of LoadReports().
Also note that you are using it wrong in the rest of your code. You know that the remainder of LoadReports() runs on the UI thread, you used Invoke(). No point in testing it again, including inside SetCtlVisible().
The typical reason for getting the bomb is because the thread is running LoadReports() too soon, before the form's window is created. You need to interlock that. The form's Load event is the signal.

Threading Method Question

I'm using the following method to show a modeless Message Box.
public void ShowMessageBox(string Message)
{
var thread = new Thread(
() =>
{
MessageBox.Show(Message);
});
thread.Start();
}
The "() => {...}" is something I've never seen before. What is the name for this code pattern?
Also, thread.Start starts the thread, and it automatically closes once the "()=>{...}" method completes (when the Message Box is OK'ed), right? If so, can you please point me to some official documentation saying that the thread closes automatically?
Thanks!
It's the lambda operator, and read as "goes to". MSDN has a good intro: Lambda Expressions (C# Programming Guide)
One concern with your example is that you're spinning up a new thread to update the UI, the UI is intrinsically single-threaded, so background updates are generally the wrong thing to do (unless you're manually/explicitly checking InvokeRequired and calling Invoke() as needed.
Regarding the UI threading...
In WinForms every Form or Control is created on a particular thread (the "UI Thread"), and you can think of that thread as owning that control (not exactly correct, but a good way to conceptualize it). Updating the UI from that thread is safe, updating the UI from another thread runs the risk of collisions and corruption and all the usual risks of parallel/async programming.
...So... how do you safely update the UI from a background thread without blocking the UI? In short--you can't--the best you can do is block it for the bare minimum required to update the UI. This is where InvokeRequired and Invoke() come in...
Here's a sample: you should be able to drop this into the code-behind of a new form with a button and textbox.
To use:
Try commenting out either the call to SetTextAsyncSafe() or SetTextAsyncSafe() -- running both could confuse you since they won't necessarily execute in the order they're called (they're running async, remember?).
Then set a breakpoint on SetText(). You should see the "safe" call will actually call the method twice--the first call will detect InvokeRequired and will call the method a 2nd time for the correct thread by Invoke()'ing to it.
You should see an Exception thrown when SetTextAsyncUnsafe() actually gets to the textBox1.Text = value; statements. The exception will be an InvalidOperationException with a message stating "Cross-thread operation not valid" -- you can google this term for more details.
The code:
private void button1_Click(object sender, EventArgs e)
{
SetTextAsyncSafe("This update was made from the UI Thread by using Invoke()");
SetTextAsyncUnsafe("This update was made directly from the background thread and can cause problems");
}
private void SetTextAsyncUnsafe(string value)
{
new Thread(() => SetText(value, false)).Start();
}
private void SetTextAsyncSafe(string value)
{
new Thread(() => SetText(value, true)).Start();
}
private void SetText(string value, bool checkInvokeRequired)
{
if (checkInvokeRequired)
{
if (InvokeRequired)
{
Invoke(new Action(() => SetText(value, checkInvokeRequired)));
return; // early exit
}
}
textBox1.Text = value;
}
That is a Lambda. In this case, you're using it to create a new anonymous method that will be run when the new Thread is started.
It's the (near) equivalent of:
public void ShowMessageBox(string Message)
{
var thread = new Thread(ShowBox);
thread.Start(Message);
}
public void ShowBox(object message)
{
MessageBox.Show(message.ToString());
}
This is called a Lambda Expression. You can read more here.
Lambda expression, C# version 3 feature.
Don't use this code. A message box needs a parent window, something it can make sure to be on top of. It can normally find a parent by itself by iterating the windows that were created on the same thread. Not in this case though, there are no other windows, it has to pick the desktop window as the parent.
That will go wrong badly when the user is working in an app window or switches focus to another app, the message box disappears behind the foreground window. There is no obvious way for the user to tell that it is there, she'll just loses sight of it. It could be hours, if not days, before she finds it back. That thread is meanwhile consuming resources badly, you would probably never consider it if you knew that this message box requires a megabyte of memory. In extreme cases, you'll crash the program with OOM.
The common alternative in Windows UI programming is a balloon tooltip provided by a NotifyIcon. Or your own form with the TopMost property set to True so it cannot easily get lost. Also allows you to control the position, important for "non-modal" notifications that should not get in the way. Set that form's ShowWithoutActivation property to true in the form constructor so it doesn't steal the focus.
Its a statement lambda.
Yes, thread is active as long as this anonymous method is running. Since after MessageBox.Show() there is no other statements, thread will exit, and this must be true... if you are in doubt add this before start:
thread.Name = "LALALA";
And then debug your app. When the message box apear, pause execution, go to Threads View and you will see LALALA running. Click OK and pause again, there should be no "LALALA"... =)

Categories