invoke during background worker - c#

I need to invoke this: string input_ip_r = listView1.Items[lc].SubItems[1].Text;
so I used
if (InvokeRequired)
{
this.Invoke(new MethodInvoker(function));
return;
}
This worked but now I have put it into a BackgroundWorker and using this
if (InvokeRequired)
{
this.Invoke(new MethodInvoker(bw.RunWorkerAsync));
return;
}
it gives an error that you can only run BackgroundWorker one at a time.
So how do I invoke while in the Backgroundworker?

1) Don't put RunWorkerAsync as the method to invoke. It's not actually running the method that you think. What you should really put there is something like this:
this.Invoke(new MethodInvoker(MethodToUpdateUI));
MethodToUpdateUI should be some new method that you create that specifically does whatever UI updates should be made in this context.
2) There's no need for InvokeRequired. You're in a background thread. Invoking will always be required.
To be honest, the entire patter of if(invoke required) call myself else do stuff is an odd construct which I dislike. InvokeRequired should pretty rarely be used. You should almost always know whether you're in the UI thread or a background thread, if you don't, chances are something wrong (either you're always in one or the other and you just don't know which, or it shouldn't be non-deterministic). Usually this means having methods that must be run in the UI thread. If you're already in the UI thread you just call them, if you're in a background thread and know it then you call Invoke first.
On top of that, Invoke works just fine even if you call it when you're already in the UI thread, so there's really no significant negative consequences to just calling Invoke regardless of whether you're in a background thread or already in the UI thread.
3) Usually it's best to separate code for solving business problems from UI code. It's code smell to be invoking from within DoWork's handler. If this is right near the end, you should probably be adding an event handler to RunWorkerCompleted. If you're calling this periodically to update the UI with progress of the worker, you should be using ReportProgress and handling the ProgressReported event. For getting info from the UI for use in a long running task you should access it before starting the background task. For exceptional cases that aren't any of those, it may be appropriate to use Invoke, but the remaining cases ought to be rare.

I'm not quite sure how you want to use the values, but just to give you an example, you could easily just do this in the BackgroundWorker thread:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
string input_ip_r = "";
this.Invoke(new Action(() =>
{
// Don't know what "lc" is (a loop variable?)
input_ip_r = listView1.Items[lc].SubItems[1].Text;
}));
}
See this answer for other ways of doing the same (this is for >= .Net 3.5)

Related

Dispatcher.CurrentDispatcher.BeginInvoke Not Invoking

I have a FileSystemWatcher and the events raised by this when a watched file changes are raised on a different thread from the UI thread. To avoid and cross-thread acess volation fun, I am attempting to use
public void RaisePathChanged(object sender, RenamedEventArgs e)
{
Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
{
// Some code to handle the file state change here.
}));
}
This compiles fine and the RaisePathChanged is fired as it should be. However, the code inside the delegate Action(() => { /*Here*/ }) never gets called/invoked, the code is merely skipped.
Why is the code being skipped, how can I fix it and is this the best way to insure code is run on the thread that created it in WPF?
Thanks for your time.
You are mixing up things.
Dispatcher.CurrentDispatcher is not the same as Application.Current.Dispatcher.
The second one is the one you seem to be looking for.
Take a look at this.
Dispatcher.CurrentDispatcher vs. Application.Current.Dispatcher
Try it out with application dispatcher.
Dispatcher.CurrentDispatcher is the dispatcher of the "current" thread - in this case the thread RaisePathChanged is executing on.
When you say Dispatcher.CurrentDispatcher .NET will create a new dispatcher if there was none.
It will however not Run() said dispatcher!
So when you schedule something on it (with BeginInvoke) it will not actually be executed unless that dispatcher is running.
That likely answers your first question (Why is it not Invoking?)
To avoid cross-thread access violation, you need the dispatcher of the thread that created whatever you are trying to protect and make sure it is a running dispatcher.
If whatever you are trying to protect was created on the default GUI thread, then use Application.Current.Dispatcher like the previous answer says, otherwise you will need to do a bit more explaining and post a bit more code before we can answer your second question. http://www.diranieh.com/NET_WPF/Threading.htm has a pretty short intro into the subject.

Updating controls in Main Thread using EventHandler

I'm using this to update a control in the main thread from another thread:
private void ShowHourGlassSafe(bool visible)
{
this.Invoke((EventHandler)((s, ev) => pictureBoxHourGlass.Visible = visible));
}
I wonder what are the implications of doing it this way or if is there any risk this is going to fail?
From the many examples are there for the same thing I could not find one like this.
It could be that it's simply wrong?
Well, you've picked a rather odd delegate to choose, as you've chosen one that has two parameters despite the fact that none are needed nor will be provided. I don't know if that will cause it to break, but it's certainly doing nothing to help. You're most likely best off using a delegate that takes no parameters and returns no values, such as:
private void ShowHourGlassSafe(bool visible)
{
this.Invoke((MethodInvoker)(() => pictureBoxHourGlass.Visible = visible));
}
Other than that, the fundamental concept of what you're doing is perfectly fine.
Typical problems with this kind of code:
You'll deadlock if the UI thread is doing something unwise like waiting for the thread to complete. There's no point in using Invoke, that blocks the worker thread for no benefit, just use BeginInvoke. Solves the deadlock potential and the unnecessary delay.
You'll crash when the UI was closed and pictureBoxHourGlass was disposed. Ensuring that the thread is no longer running before allowing the UI to close is very commonly overlooked. Just displaying an hour glass isn't enough, you also have to take countermeasures to prevent the user from closing the UI. Or otherwise interlock it with a way to cancel the thread first
The user will typically be befuddled when an hour glass shows up without him doing anything to ask that something gets done. The 99% correct case is that you display the hour glass with code in the UI thread and then start the thread. And hide it again when the thread completes. Easiest to do with the BackgroundWorker or Task classes, they can run code on the UI thread after the job was done.
Favor the Action delegate types for consistency:
private void ShowHourGlassSafe(bool visible) {
this.BeginInvoke(new Action(() => something.Visible = visible));
}

Why do I not get the "Cross-thread operation not valid" error

I use a BackgroundWorker and do this:
private void loadNewAsyncToolStripMenuItem_Click(object sender, EventArgs e)
{
this.Text = "RunWorkerAsync()";
backgroundWorkerLoading.RunWorkerAsync();
}
private void backgroundWorkerLoading_DoWork(object sender, DoWorkEventArgs e)
{
UnsafeThreadMethod("hello");
EvenUnsaferThreadMethod();
}
And now the two methods.
private void UnsafeThreadMethod(string text)
{
toolStripLabelRssFeedData.Text = text;
}
private void EvenUnsaferThreadMethod()
{
panelLoading.Visible = true;
}
I don't understand why UnsafeThreadMethod doesn't throw the following exception but EvenUnsaferThreadMethod does.
Cross-thread operation not valid: Control 'panelLoading' accessed from a thread other than the > thread it was created on.
According to the message it's because toolStripLabelRssFeedData was created on the same thread but it wasn't.
I thought that I can't call controls created by the main thread and have to use the ProgressChanged event. What's going on?
And I have a second question. What is the advantage of doing it like this when I can use ProgressChanged? What should I do?
private void EvenUnsaferThreadMethod()
{
if (panelLoading.InvokeRequired)
{
panelLoading.Invoke(new MethodInvoker(() => { EvenUnsaferThreadMethod(); }));
}
else
{
panelLoading.Visible = true;
}
}
To the first question:
the cross-thread exception is deliberately thrown in Debug mode. This means there is (conditional) code checking on InvokeRequired built into most of the GUI controls. Like the Panel.
Apparently the ToolstripLabel does not make this check. Since it does not derive from Control that could be because it is outside the scope of this safety net.
Since the standard disclaimer "Any instance members are not guaranteed to be thread safe" applies to the ToolstripLabel I would just go with the normal InvokeRequired logic when setting the Text.
For your first question, I am not entirely sure, but a review from online seems to show that sometimes this will not throw an exception, but it will not update the label. Is that the case here? Is your label being updated along with having no exception?
However, I can answer you second question right now. The ProgressChanged event is meant for exactly what it sounds like. It is supposed to be called to let the UI thread know the status of the backgroundworker so that it can update itself appropriately. The original calling thread (UI in this case) is the one that is used for the ProgressChanged, so when it updates it does not need to call Invoke. But, this should really only be done for showing the progress of a background worker.
Now, if it is not an update that you are trying to pass to the calling method, then I would suggest just passing your return data back through the RunWorkerCompleted event. This passes all of your final data back up to the original (UI) thread, so that it can update the UI without any need for an Invoke.
So, yes your call to Invoke will work, though. However, understanding what each of the other events are for can help you understand why to use one way over another. Maybe a ProgressChanged event fits better? It can also declutter your code from having unnecessary invokes.
Update to first q
I still cannot find anything about the toolstrip not needing the invoke. In fact I am finding the opposite using google searches like "toolstriplabel no cross thread exception" or "toolstriplabel invoke", etc. However, as henk mentioned, the toolstriplabel doesn't inherit from control so that might explain why no invoke is required. However, my suggestion is to assume that it will act like any other UI control and make sure it is updated on the UI thread to be safe. do not rely on quirks. Better safe than sorry, you never know if things like this might change, especially since it is logically a UI item to most..,
The advantage of your second choice is that it works :)
All UI elements are created on main UI thread and, what is more important from this question perspective, is that can be acessed only within that thread.
This is the reason why your first case fails and that is the reason your second case will work. Invoke()... will redirect required merhod call to the main UI thread.
Hope this helps.

How to Make Sure UI is Responsive Using BackgroundWorker

Is BackgroundWorker in c# Thread Safe?
The reason I ask this is because I get a
Controls created on one thread cannot
be parented to a control on a
different thread
exception with it. This is my DoWork event code:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
var openFile = document.Open(MyFileName);
e.Result = openFile;
}
where document is an UI control that is initialized when the parent form is created. During Open method various properties in document will be filled.
I tried to change the code to invoke, yet the same problem persists. i.e,
document.GetType().GetMethod("Open)".Invoke(document, new object[]{MyFileName})
will yield the same error as the above.
Any idea how to manipulate the document control? In other words, how to make the above code work?
Edit: It was suggested that I use Control.Invoke, but it still didn't work ( both of the threads hanged). This is the code I tried:
private delegate bool OpenFile(string filePath);
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
OpenFile oF = new OpenFile(document.Open);
var openFile = Invoke(oF, MyFileName); // it doesn't really matter whether I use BeginInvoke or Invoke, or other Control.Invoke, the end result is the same. Both the main thread hosting the document and the thread that launches the UI hanged.
e.Result = openFile;
}
It isn't the thread that's the problem it's the fact that it's trying to call a method on a UI control. In both WPF and WinForms controls can only be called on the UI thread (of which there is typically one). You don't say which you are using but you need to call the Control.Invoke method for WinForms or Dispatcher.Invoke for WPF.
The Invoke() reflection method you show will actually invoke the method on the current thread.
You can either invoke as Mehrdad Afshari suggested, or you can make use of the bgw's progress event which comes back on the UI thread. Or the work completed event which also comes back on the UI thread. The difference between the two is WorkCompleted is fired only once at the end. Progress is fired by you from DoWork.
While it's unclear to me what you exactly mean by thread-safety of a BackgroundWorker, the problem is not that object; Windows Forms controls are designed to be manipulated on a single thread (the UI thread). You should not manipulate Windows Forms objects on different threads. You can invoke actions in the UI thread from other threads by using the Control.Invoke method (the Invoke method you are currently using is provided by reflection and is totally unrelated to this problem):
Invoke(new Action(MethodToRunInUIThread));
void MethodToRunInUIThread() {
// do stuff here.
}
By the way, it doesn't make sense to use a background worker if all you are doing is manipulating UI objects.
If that functionality of the UI Control takes that long to execute, there may not be much you can do. "Freezing" occurs when a long-running operation happens on the UI thread, and if that function of the control was not specifically made thread-safe, it must be run on the main thread.
Normally, you'd want to separate the "document" functionality away from the control that displays it. This way, your document could be loaded on a separate, independent thread and be displayed later when ready. Otherwise, the control itself would have to implement a multi-threaded load routine to slow loading freezes.
Since you've specified this is a third party control in your comments, you may be out of luck here.
BackgroundWorker is a thread based structure. The thread-safety matter is about functions when doing simultaneous tasks. Maybe what you ask for is about winforms controls which are accessed through a unique thread, that of the user interface thread.
You need to use Control.BeginInvoke() in DoWork. This executes the delegate asynchronously and so will ensure the calling thread will not "hang".
Control.Invoke() will execute the delegate on the other thread also, but will cause the calling thread to wait for it to complete.
Generally in Windows Forms you are better off using Control.BeginInvoke() wherever possible to help avoid deadlocking between threads that can occur when one thread waits for another, as with Control.Invoke().
If the "document" object inherits from System.Windows.Forms.Control, you can simply call document.BeginInvoke(myDelegate).
However if it is actually some other component that encapsulates GUI controls, it may expose some way to call BeginInvoke. Check the documentation (if any). If there is no such ability, then unfortunately it is probably just not designed to support multi-threaded applications.
It looks like you are confused about the various Invoke/BeginInvoke types (understandable). This earlier question: What is the difference between Invoke and BeginInvoke? and Jon Skeets answer should help clarify things.
#Graviton, a related task with an answer is found here. The person was using BackgroundWorker to update a textbox, same concept applies (yours is only a single worker thread).

Any solution to Illegal Cross Thread Operation exception?

When you data bind in C#, the thread that changes the data causes the control to change too. But if this thread is not the one on which the control was created, you'll get an Illegal Cross Thread Operation exception.
Is there anyway to prevent this?
You should be able to do something like:
if (control.InvokeRequired)
{
control.Invoke(delegateWithMyCode);
}
else
{
delegateWithMyCode();
}
InvokeRequired is a property on Controls to see if you are on the correct thread, then Invoke will invoke the delegate on the correct thread.
UPDATE: Actually, at my last job we did something like this:
private void SomeEventHandler(Object someParam)
{
if (this.InvokeRequired)
{
this.Invoke(new SomeEventHandlerDelegate(SomeEventHandler), someParam);
}
// Regular handling code
}
which removes the need for the else block and kind of tightens up the code.
As I don't have a test case to go from I can't guarantee this solution, but it seems to me that a scenario similar to the one used to update progress bars in different threads (use a delegate) would be suitable here.
public delegate void DataBindDelegate();
public DataBindDelegate BindData = new DataBindDelegate(DoDataBind);
public void DoDataBind()
{
DataBind();
}
If the data binding needs to be done by a particular thread, then let that thread do the work!
If the thread call is "illegal" (i.e. the DataBind call affects controls that were not created in the thread it is being called from) then you need to create a delegate so that even if the decision / preparation for the DataBind is not done in the control-creating thread, any resultant modification of them (i.e. DataBind()) will be.
You would call my code from the worker thread like so:
this.BindData.Invoke();
This would then cause the original thread to do the binding, which (presuming it is the thread that created the controls) should work.
In WPF and Silverlight the binding infrastructure takes care of the switching to the UI thread.

Categories

Resources