Threading issue -- with no threads?!?

T

TomB

Feel free to mock me....

I'm trying to figure out the Model View Controller pattern and thought I'd
try building a "simple" example.

I decided to go with a car.

The model is the engine, the dashboard the view and the pedals and steering
the controller.

My Engine has a System.Timers.Timer in it that fires every couple of
seconds. When it fires i calculated the fuel used and raise an event.

My dashboard catches this event and updates the fuel gauge.

I'm getting an error that I can't update the UI from a background thread.
("Cross-thread operation not valid: Control 'pbFuel' accessed from a thread
other than the thread it was created on.")

What's the background thread? The timer?

Thanks
Tom B
 
N

Naveen

Yes, the timer is thread pool thread and you cannot update the UI
thread from any other thread. Use the Form's Invoke or BeginInvoke
method to update the forms properties.
 
J

Jason Hales

Additionally, if you don't mind binding your Engine's timers to the
Forms namespace you could use a Timer from System.Windows.Forms.

That way you'll still be in the same thread when the event fires and
not have to worry about Invoke/InvokeRequired.
 
J

Jason Hales

TomB, just a bit of background info for you. When doing any sort of
multithreading work you must be careful to ONLY communicate with
controls using the thread that created them - this is a fundamental
concept relating to performace and thread synchronisation

A Timer from System.Timers will result in a thread from the Thread Pool
being used which must use Invoke to commuicate with your control
'pbFuel' - Invoke/BeginInvoke ensures that the call is marshalled along
to the correct thread.

As mentioned earlier, using a Timer from System.Windows.Forms will
result in the same thread being used but will block your app until it
finishes but you don't need to worry about Invoke.

The same Invoke rules apply to using a Thread object and
ThreadPool.QueueUserWorkItem.

If in doubt always check the InvokeRequired property on your control
and the use Invoke or BeginInvoke. In the following example
InvokeUpdateLabel invokes itself so that the label can be set :

public void InvokeUpdateLabel()
{
if (this.label1.InvokeRequired)
{
this.label1.Invoke(new
MethodInvoker(InvokeUpdateLabel));
}
else
{
this.label1.Text = DateTime.Now.ToLongTimeString();
}
}

This is a good book on the subject: .NET Multhreading by Alan L Dennis
(ISBN 1-930110-54-5)
 
S

Stoitcho Goutsev \(100\)

TomB,

Setting a reference to a control from your UI (any control) into the
System.Timers.Timer.SynchronizingObject property will marshal the timer's
Elapsed event to the main UI thread and you won't have this threading issue.
 
T

TomB

Thanks everyone.

I understand that items from a separate thread can't update the UI. Which
makes sense.

Where I'm confused is in the fact that my engine object is what contains the
timer. When the timer elapses (still in the engine object) it does some
calculations then raises an event. The form "catches" the event and is
updating itself.

Clearly there's some stuff going on with the threading that I'm not getting.

Jason -- your sample code was a great help.

Thanks again

TomB
 
W

William Stacey [MVP]

When the timer calls your callback in your engine object, that is still a
ThreadPool thread. When you call the event (i.e. delegate) in your timer
callback, you are still running any delegates in the Event (in order) using
that same ThreadPool thread - so you still need to BeginInvoke in the forms
event method for anything that will update the UI. Does that help?

--
William Stacey [MVP]

| Thanks everyone.
|
| I understand that items from a separate thread can't update the UI. Which
| makes sense.
|
| Where I'm confused is in the fact that my engine object is what contains
the
| timer. When the timer elapses (still in the engine object) it does some
| calculations then raises an event. The form "catches" the event and is
| updating itself.
|
| Clearly there's some stuff going on with the threading that I'm not
getting.
|
| Jason -- your sample code was a great help.
|
| Thanks again
|
| TomB
|
|
| | > TomB, just a bit of background info for you. When doing any sort of
| > multithreading work you must be careful to ONLY communicate with
| > controls using the thread that created them - this is a fundamental
| > concept relating to performace and thread synchronisation
| >
| > A Timer from System.Timers will result in a thread from the Thread Pool
| > being used which must use Invoke to commuicate with your control
| > 'pbFuel' - Invoke/BeginInvoke ensures that the call is marshalled along
| > to the correct thread.
| >
| > As mentioned earlier, using a Timer from System.Windows.Forms will
| > result in the same thread being used but will block your app until it
| > finishes but you don't need to worry about Invoke.
| >
| > The same Invoke rules apply to using a Thread object and
| > ThreadPool.QueueUserWorkItem.
| >
| > If in doubt always check the InvokeRequired property on your control
| > and the use Invoke or BeginInvoke. In the following example
| > InvokeUpdateLabel invokes itself so that the label can be set :
| >
| > public void InvokeUpdateLabel()
| > {
| > if (this.label1.InvokeRequired)
| > {
| > this.label1.Invoke(new
| > MethodInvoker(InvokeUpdateLabel));
| > }
| > else
| > {
| > this.label1.Text = DateTime.Now.ToLongTimeString();
| > }
| > }
| >
| > This is a good book on the subject: .NET Multhreading by Alan L Dennis
| > (ISBN 1-930110-54-5)
| >
|
|
 
T

TomB

It does, thanks.

I'm curious though. If i decided to sell my engine object as a black box
type of thing; how would my purchaser know that the event is being raised on
a separate thread?

Just a reminder -- the only reason I'm doing this at all is to wrap my head
around the idea behind model-view-controller.

Wait, a sec. When my timer "fires" in my engine object -- could I force
that back onto the main thread? Rather than worrying about it in my view
(the Form)?

Thanks again for your input.

Tom
 
S

Stoitcho Goutsev \(100\)

TomB,

All that is in the docs. When you buy a blackbox you first read the
instructions.
 
B

Brian Gideon

TomB said:
Wait, a sec. When my timer "fires" in my engine object -- could I force
that back onto the main thread? Rather than worrying about it in my view
(the Form)?

Tom,

Yes. Use the same pattern that the System.Timers.Timer class uses.
That is provide a SynchronizingObject property that accepts an
ISynchronizeInvoke object. If you're already using the aforementioned
timer it would be really easy. Something like the following.

public class Engine
{
private System.Timers.Timer timer = new System.Timers.Timer();

public ISynchronizeInvoke SynchronizingObject
{
get { return timer.SynchronizingObject; }
set { timer.SynchronizingObject = value; }
}
}

Brian
 
W

William Stacey [MVP]

I don't see SynchronizingObject in the System.Threading.Timer class in 2.0

--
William Stacey [MVP]

| public class Engine
| {
| private System.Timers.Timer timer = new System.Timers.Timer();
|
| public ISynchronizeInvoke SynchronizingObject
| {
| get { return timer.SynchronizingObject; }
| set { timer.SynchronizingObject = value; }
| }
| }
|
| Brian
|
 
B

Brian Gideon

That's System.Timers.Timer. As you indicated the
System.Threading.Timer does not support marshaling via
ISynchonizeInvoke objects.
 
S

Stoitcho Goutsev \(100\)

BTW, in .NET 2.0 they have new system of marshaling calls to the UI thread.
Look at the BackgroundWorker class, it doesn't have SynchronizigObject
property. I haven't dug into the internals, but What I've seen I think they
send messages directly to the thread. Anyways, in order to marshal the call
runing a message pump in the target thread is a must.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top