views:

1167

answers:

4

Hi,

I realise that I can't access Form controls from the DoWork event handler of a BackgroundWorker. (And if I try to, I get an Exception, as expected).

However, am I allowed to access other (custom) objects that exist on my Form?

For instance, I've created a "Settings" class and instantiated it in my Form and I seem to be able to read and write to its properties.

Is it just luck that this works?

What if I had a static class? Would I be able to access that safely?

+1  A: 

You should communicate to the user interface through the ProgressChanged and RunWorkerCompleted events (and never the DoWork() method as you have noted).

In principle, you could call IsInvokeRequired, but the designers of the BackgroundWorker class created the ProgressChanged callback event for the purpose of updating UI elements.

[Note: BackgroundWorker events are not marshaled across AppDomain boundaries. Do not use a BackgroundWorker component to perform multithreaded operations in more than one AppDomain.]

MSDN Ref.

Mitch Wheat
Thank you. So are you saying I must never ever access my Settings object in my Form from the BackgroundWorker? The only reason I question this is because it worked. I thought .NET was always meant to give you a CrossThreadingException?
James
It will give the cross threading exception if it happens that its not the UI thread handling exceution at that point. So without explicit marshalling onto the UI thread (via IsInvokerequired and subsequent Invoke if required) it is unsafe to access UI elements.
Mitch Wheat
Oh I see, so in my scenario either the UI thread or the BGW thread may be called upon to update the contents of my Settings object?
James
A: 

You can't access controls that where created in one thread from another thread. You can either use Settings class that you mentioned, or use InvokeRequired property and Invoke methods of control.

I suggest you look at the examples on those pages:

http://msdn.microsoft.com/en-us/library/ms171728.aspx

http://msdn.microsoft.com/en-us/library/system.windows.forms.control.invokerequired.aspx

Alex Reitbort
>You can't access controls that where created in one thread from another thread.Yes, you can. You will just need to invoke it i.e. if(listbox1.InvokeRequired) listbox1.Invoke(new MethodInvoker(delegate { ... }));else ...
SnOrfus
Isn't it what I wrote?
Alex Reitbort
+1  A: 

Ok, I've done some more research on this and I think have an answer. (Let the votes decide if I'm right!)

The answer is.. you can access any custom object that's in scope, however your access will not be thread-safe.

To ensure that it is thread-safe you should probably be using lock. The lock keyword prevents more than one thread executing a particular piece of code. (Subject to actually using it properly!)

The Cross Threading Exception that occurs when you try and access a Control is a safety mechanism designed especially for Controls. (It's easier and probably more efficient to get the user to make thread-safe calls then it is to design the controls themselves to be thread-safe).

James
+2  A: 

@Engram:

You've got the gist of it - CrossThreadCalls are just a nice feature MS put into the .NET Framework to prevent the "bonehead" type of parallel programming mistakes. It can be overridden, as I'm guessing you've already found out, by setting the "AllowCrossThreadCalls" property on the class (and not on an instance of the class, e.g. set Label.AllowCrossThreadCalls and not lblMyLabel.AllowCrossThreadCalls).

But more importantly, you're right about the need to use some kind of locking mechanism. Whenever you have multiple threads of execution (be it threads, processes or whatever), you need to make sure that when you have one thread reading/writing to a variable, you probably don't want some other thread barging and changing that value under the feet of the first thread.

The .NET Framework actually provides several other mechanisms which might be more useful, depending on circumstances, than locking in code. The first is to use a Monitor class, which has the effect of locking a particular object. When you use this, other threads can continue to execute, as long as they don't try to lock that same object. Another very useful and common parallel-programming idea is the Mutex (or Semaphore). The Mutex is basically like a game of Capture the Flag between your threads. If one thread grabs the flag, no other threads can grab it until the first thread drops it. (A Semaphore is just like a Mutex, except that there can be more than one flag in a game.)

Obviously, none of these concepts will work in every particular problem - but having a few more tools to help you out might come in handy some day :)

Mike
Thanks for your input Mike. Very useful.
James