Threads Question

B

Bruce C. Miller

This might be an easy one (just starting to learn C# here), but I'm
overlooking it for some reason...

namespace test2
{
public class Form1 : System.Windows.Forms.Form
{
public Graphics g;
private Thread t1;

// some other code

private void Form_Paint(object sender,
System.Windows.Forms.PaintEventArgs e)
{
g = e.Graphics;

Trace.WriteLine(g.RenderingOrigin.ToString());

t1 = new Thread(new ThreadStart(Wait));
t1.Start();
}

public void Wait()
{
Trace.WriteLine(g.RenderingOrigin.ToString()); //runtime error
}
}
}

I get a System.ArgumentException when the second Trace is performed.
 
N

Nicholas Paldino [.NET/C# MVP]

Not only that, but it is very possible that by the time the code in the
thread runs, the Graphics instance pointed to by g would have been disposed
of (which would cause an error when RenderingOrigin is called).

Hope this helps.
 
B

Bruce C. Miller

Nicholas said:
Not only that, but it is very possible that by the time the code in the
thread runs, the Graphics instance pointed to by g would have been disposed
of (which would cause an error when RenderingOrigin is called).

Hope this helps.

Thanks. What I was trying to do in my original application was to
create do some initial drawing on my form in the Form_Paint method,
then start a thread to change this display as time passed.

A good example of this (though not what I was trying to do) would be a
GDI clock app that used a thread to update the hands.

I guess I'm approaching this wrong... :\
 
N

Nicholas Paldino [.NET/C# MVP]

Bruce,

For this kind of application, you can definitely have another thread do
processing, and then update the main thread. However, the way you go about
it is a little different.

Your application is really this big giant loop (on the UI thread) which
cycles over and over again, processing messages. One of these messages that
you process is the paint message, which causes your Form_Paint event handler
to be called. The key is getting information into that routine when it is
called, and calling that routine when something has changed.

Now, in order to do this, you can start your thread. As your new thread
runs, it can update state in the object (somewhere that the event handler
has access to without it being passed in, like a field in the same object).
Because you are accessing this value on a different thread, you want to make
sure you place a lock section around the setting/getting of this value, so
that you don't corrupt it if two threads are trying to access it at the same
time.

Once you set the value, you have to notify the main UI to repaint
itself. You can do this through a call to the Invalidate method on the main
UI component of your app (which your thread has to have access to somehow).
Fortunately, Invalidate is a method that you can call from any thread.

Once that happens, your paint event handler will be called. It will be
generic, painting itself according to whatever the current state is. In
this case, you would look at the field that has the time, and then paint the
appropriate display. You don't really care what the thread is doing, only
drawing yourself to reflect the current state.
 
A

Ajay Kalra

the Graphics instance pointed to by g would have been disposed
of (which would cause an error when RenderingOrigin is called).

I want to clarify if this is correct? I would have guessed GC was smart
enough to *not* dispose it off as a reference to g *is* surely going to
happen.
 
N

Nicholas Paldino [.NET/C# MVP]

Ajay,

Disposing and GC are different things. The Graphics class implements
the IDisposable interface, because it has a handle to a Device Context.
After the event handler is called, the Dispose method on that instance is
most likely called, disposing of the unmanaged DC, and invalidating the
handle. The object is still around, but the handle is worthless.
 
J

Jon Skeet [C# MVP]

Ajay Kalra said:
I want to clarify if this is correct? I would have guessed GC was smart
enough to *not* dispose it off as a reference to g *is* surely going to
happen.

Disposing of something is very different from garbage collecting it.
While g is still a "live" reference, there's nothing to say that the
code which called your paint event handler won't have had Dispose
called on it.
 
A

Ajay Kalra

Thanks Jon and Nicholas. I guess I need to do some more experimentation
on my end.
 
B

Bruce C. Miller

Nicholas said:
Bruce,

For this kind of application, you can definitely have another thread do
processing, and then update the main thread. However, the way you go about
it is a little different.

Your application is really this big giant loop (on the UI thread) which
cycles over and over again, processing messages. One of these messages that
you process is the paint message, which causes your Form_Paint event handler
to be called. The key is getting information into that routine when it is
called, and calling that routine when something has changed.

Now, in order to do this, you can start your thread. As your new thread
runs, it can update state in the object (somewhere that the event handler
has access to without it being passed in, like a field in the same object).
Because you are accessing this value on a different thread, you want to make
sure you place a lock section around the setting/getting of this value, so
that you don't corrupt it if two threads are trying to access it at the same
time.

Once you set the value, you have to notify the main UI to repaint
itself. You can do this through a call to the Invalidate method on the main
UI component of your app (which your thread has to have access to somehow).
Fortunately, Invalidate is a method that you can call from any thread.

Once that happens, your paint event handler will be called. It will be
generic, painting itself according to whatever the current state is. In
this case, you would look at the field that has the time, and then paint the
appropriate display. You don't really care what the thread is doing, only
drawing yourself to reflect the current state.

This was precisely what I was confused about. My practice program is
working great now. Thanks alot :)

Oddly enough, if you perform some operation similar to my original
example, with an integer instead of the Graphics object, it works fine.
I suppose that is because g was a pointer to an object in the UI
thread. Still seems a little weird to me.
 
J

Jon Skeet [C# MVP]

Bruce C. Miller said:
This was precisely what I was confused about. My practice program is
working great now. Thanks alot :)

Oddly enough, if you perform some operation similar to my original
example, with an integer instead of the Graphics object, it works fine.
I suppose that is because g was a pointer to an object in the UI
thread. Still seems a little weird to me.

It's not odd at all. UI elements are "thread hostile", or have "thread
affinity". To help make them fast (which UI elements need to be) they
haven't been written to be thread-safe at all. The basic rule is that
you can't touch them (other than calling CreateGraphics, Invoke,
BeginInvoke, EndInvoke or InvokeRequired on controls) on a thread other
than the one they were created on.
 

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