tags:

views:

111

answers:

3

I have a method that processes thousands of items and it takes a long time.

What is the "right" way to have the method that is doing the long operation report back its progress as it does this?

For example, if I wanted to have a progress bar in my UI show the progress of some long running method in another thread.

+3  A: 

Use a BackgroundWorker.

There are several SO questions on the BackgroundWorker, for example:

Mitch Wheat
Isn't this a bit wasted in just updating the UI?Wouldn't this be a better solution for splitting up and threading the long running process, not simply reporting from 1 long running process?
Tim Jarvis
@Tim Jarvis: that's what's I was implying...
Mitch Wheat
+1  A: 

In this particular case I would be inclined to create a custom event and fire it from the long running process, and just subscribe whatever UI elements you want updated to the event in event handlers. (UI is always updated in the main UI thread anyway)

Tim Jarvis
Custom events have the downside that they need to be marshalled to the thread of the subscriber, which involves writing a few extra lines of code. But the BackgroundWorker handles that perfectly for you.
Aviad P.
I doubt merely having a progress bar on a locked up UI will placate the user, so I think it's safe to assume the OP wants a separate thread as well.
ProfK
@Aviad, @ProfK, ever heard of KISS? BackgroundWorker is not the answer to every question about long running code. It's good to see others (ie Tim) also realise there are alternatives.
Ash
"UI is always updated in the main thread anyway" - this is not automatic. You will need to use Control.Invoke to update UI elements in an event handler fired from a background thread. Or use a BackgroundWorker, that takes care of this for you.
Joe
+1  A: 

A long-running task, whether running in a dedicated background thread or not, may want to communicate with the caller in one of two ways:

  • To report progress (with a BackgroundWorker this corresponds to calling BackgroundWorker.ReportProgress)

  • To check if cancellation has been requested (with a BackgroundWorker this corresponds to examining the CancellationPending property).

It's sometimes useful for a long-running process to be able to communicate with the caller in this way without knowing or caring if it is being run from a BackgroundWorker. I have therefore created an interface IProgress which abstracts this functionality with a method ReportProgress and a property CancellationPending. I have an enhanced version of BackgroundWorker that implements this interface, and also alternative implementations that do not use a BackgroundWorker.

For example, a console application might run a long-running task in the foreground. Its implementation of IProgress.ReportProgress could e.g. use Console.WriteLine if running in verbose mode. Its implementation of IProgress.CancellationPending could check if CTRL-C has been pressed. The same long-running task could be called from this console application and from a WinForms application that uses a BackgroundWorker.

Personally I think an abstraction like this should have been included in the .NET Framework when BackgroundWorker was designed.

Joe