views:

47

answers:

2

I haven't done much multithreading before and now find the need to do some background work and keep the UI responsive. I have the following code.

data.ImportProgressChanged += new 
     DataAccess.ImportDelegate(data_ImportProgressChanged);

Thread importThread = new Thread(
       new ThreadStart(data.ImportPeopleFromFAD));
importThread.IsBackground = true;
importThread.Start();

void data_ImportProgressChanged(int progress)
{
    toolStripProgressBar.Value = progress;
}

//In my data object I have 
public void ImportPeopleFromFAD()
{
    ImportProgressChanged(someInt);
}

But the UI doesn't get updated since the ImportProgressChanged() call is made on the background thread. In objective C I know you can use performSelectorOnMainThread and pass it a method to call using the main thread. What is the equivalent way of calling ImportProgressChanged() from the main thread?

+2  A: 

(Assuming Windows Forms.) You can use Control.Invoke or Control.BeginInvoke - but a cleaner way may be to use BackgroundWorker to start with.

In WPF you'd use a Dispatcher instead of Control.Invoke, btw. See this WPF threading model guide for more details.

EDIT: Personally I probably wouldn't bother testing InvokeRequired first - I'd just call Invoke or BeginInvoke. If you're already "on" the right thread it won't do any significant harm.

For progress bars, however, BackgroundWorker is definitely the way forward.

Jon Skeet
So there is no simple way to have 1 thread do something in another thread? The Control.Invoke solution that @Henk Holterman illustrated seems like such a mess, and specific only to controls.
jamone
toolStripProgressBar indicates WinForms
Henk Holterman
@jamone: It's not specific to controls; it's specific to anything implementing `ISynchronizeInvoke`. Basically there has to be some support from the other thread - you shouldn't just expect to be able to hijack any random thread at will...
Jon Skeet
@Jamone: you cannot break into a thread, it will have to poll (MessageLoop). Control.Invoke is the gateway, messy as it is.
Henk Holterman
I went the BackgroundWorker route.
jamone
A: 

You already have your answer but I'll add my solution anyway:

void data_ImportProgressChanged(int progress)
{
    if (InvokeRequired)
    {
        BeginInvoke(new Action<int>(data_ImportProgressChanged),progress);
        return;
    }

    toolStripProgressBar.Value = progress;
}
Chris S