Problems Running My First Background Thread

J

Jonathan Wood

I want to run a lengthy operation, yet still have the UI be responsive to
user input. Therefore, I decided to create my first multi-threaded C#
application and run the lengthy process in the background, and stop if the
main process sets a flag.

The basic approach I took is the one described at
http://support.microsoft.com/kb/815804. My code's a bit different but very
similar. Instead of manipulating a progress bar, my background thread
instead updates a label contrl named label1.

Everything works until I attempt to update the label control. I then get the
error "Cross-thread operation not valid: Control 'label1' accessed from a
thread other than the thread it was created on."

A background thread doesn't seem very useful if I can't modify the form. And
why does the Microsoft example work if this is not allowed?

Thanks for any tips!

Jonathan
 
J

Jonathan Wood

I was able to find a VB.NET example that showed how to resolve this. I had
to instance a delegate, and call it using Invoke.

I also read what seemed to indicate the Microsoft example used to work but
MS decided that instead we now need to jump through hoops to accomplish such
a simple task.

Oh well...

Jonathan
 
J

Jonathan Wood

While not documented as such, it turns out that the ProgressBar.Value
property is an exception to the rule. It uses SendMessage() to update the
current value, which handles the cross-thread issue for you. That's why
the MSDN example works and your code doesn't.

Ah. That's good to know.
In most code, when you are accessing a GUI object from a non-GUI thread,
you need to use the Control.Invoke() method to execute the code you want
to execute. So, instead of:

label1.Text = "Some string";

You need:

label1.Invoke((MethodInvoker) delegate { label1.Text =
SomeString"; });

The "(MethodInvoker)" cast provides a specific type, because the Invoke()
method doesn't require a specific delegate type and thus the compiler
can't infer one. The "delegate { ... }" part is an anonymous method; you
could invoke a named method instead, but this is my preferred style, as it
keeps all the relevant code together.

All that said, you may find that using the BackgroundWorker class works
better for you. It has a ProgressChanged event that is raised on the
correct thread, avoiding the cross-thread exception issue.

I kind of have it working now but perhaps I'll look into that.

Thanks!

Jonathan
 
J

Jonathan Wood

After studying it, I did decide to use the BackgroundWorker component.

I have mixed feelings about which is best and the BackgroundWorker still
requires me to package all my status elements into an object that I can pass
around, but it may be a little better suited to all the things I needed to
do.

Thanks again.

Jonathan

Peter Duniho said:
[...]
The basic approach I took is the one described at
http://support.microsoft.com/kb/815804. My code's a bit different but
very similar. Instead of manipulating a progress bar, my background
thread instead updates a label contrl named label1.

Everything works until I attempt to update the label control. I then get
the error "Cross-thread operation not valid: Control 'label1' accessed
from a thread other than the thread it was created on."

A background thread doesn't seem very useful if I can't modify the form.
And why does the Microsoft example work if this is not allowed?

While not documented as such, it turns out that the ProgressBar.Value
property is an exception to the rule. It uses SendMessage() to update the
current value, which handles the cross-thread issue for you. That's why
the MSDN example works and your code doesn't.

In most code, when you are accessing a GUI object from a non-GUI thread,
you need to use the Control.Invoke() method to execute the code you want
to execute. So, instead of:

label1.Text = "Some string";

You need:

label1.Invoke((MethodInvoker) delegate { label1.Text =
SomeString"; });

The "(MethodInvoker)" cast provides a specific type, because the Invoke()
method doesn't require a specific delegate type and thus the compiler
can't infer one. The "delegate { ... }" part is an anonymous method; you
could invoke a named method instead, but this is my preferred style, as it
keeps all the relevant code together.

All that said, you may find that using the BackgroundWorker class works
better for you. It has a ProgressChanged event that is raised on the
correct thread, avoiding the cross-thread exception issue.

Pete
 

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