views:

250

answers:

4

Problem description: you write a library which contains some algorithms/tasks which can take a long time to finish, for various reasons: computational, file system, network communication etc. You want to be able to:

  1. Send some progress information about the task (progress, activity logging etc.)
  2. Have a way to abort the task before completion if some external signal or property has been set.

I've implemented a framework for this, but this requires that all such tasks have to reference an assembly which contains this framework.

My question: is there an already built-in mechanism in .NET framework (3.5 or below) for the problem described above?

I know I could use events, but this would mean long running tasks would have to expose such events, which I think is an overhead. Ideally I want to have a framework which hides away multithreading issues and is dependency-injection friendly, but would not depend on an additional custom assembly and would not pollute the original interface.

I hope I described the problem well enough. If not, I can post some samples of the interfaces from my own framework.

UPDATE: OK, I think my problem description needs a bit of clarification :). When I say "long-running", I don't mean "long" in the workflow-sense. I'm working on a WinForms mapping app which does all sorts of stuff, like generating relief contours. To do this, it first has to download the elevation data files from a FTP server, unzip them and then perform some calculations. I wrote the code for this a long time ago, but in order to make it more GUI-friendly, I have to retro-fit various checks - for example, detecting that the user has clicked on the Abort button and stop the process.

So basically my concern is: how to write a code that can later (if ever) be used in a GUI environment, where you cannot simply run everything in the main GUI thread and freeze the whole application. The challenge is to find a way to make your code suitable for GUI purposes without tying it to a particular GUI platform.

+6  A: 

That sounds a lot like Windows Workflow Foundation.

Mark Seemann
WWF is a bit of an overkill for the problem I'm describing (I've updated the description).
Igor Brejc
+1  A: 

Take a look at the saga pattern. It's not built into the framework but can be implemented. Alternatively both NServiceBus and MassTransit have implementations of this. Arnon RGO has a draft from his book (will it ever be finished) describing it here: http://rgoarchitects.bit.ly/4xNwpp

In my experience getting going with NServiceBus is much simpler than WF, and is also more powerful (though I haven't looked at WF 4, which by all descriptions is a near complete rework of WF as Microsoft have recognised the failings of this).

Even if you don't want a framework like NServiceBus or MassTransit, the pattern itself, is well worth looking at as it fits your problem space very closelyfrom what you have described.

Neil
+1  A: 

It depends on how complicated your system is. For relatively simple problems, you could probably nicely use the BackgroundWorker class from .NET 2.0. It supports reporting the progress of the operation using OnProgressChanged event and it also supports cancelation of the background task using CancelAsync method.

The class is controlled by events, but since that's already a part of the class, I don't think it is any overhead for you:

var bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(RunWorkerCompleted);
bw.ProgressChanged += new ProgressChangedEventHandler(ProgressChanged);
  • The DoWork method is executed to run the background task (it can report progress by calling bw.ReportProgress and check for pending cancellation using bw.CancellationPending).

  • The RunWorkerCompleted method is executed on the GUI thread when the operation completes (which gives you a nice way to synchronize without worrying about concurrency)

  • The ProgressChanged event is triggered whenever your DoWork method reports some progress change.

For simpler problems, I believe you could represent your tasks as background workers.

Tomas Petricek
@Tomas, I already use BackgroundWorker for these purposes, but indirectly at the infrastructure level. The problem is that representing your tasks directly as BackgroundWorker class implies that it will _always_ be run in async mode, which isn't necessarily the case.
Igor Brejc
Yes - I was expecting that BackgroundWorker won't be enough powerful in your case. Hopefuly it may help to someone looking how to sovle this kind of problem in a simpler environment in the future.
Tomas Petricek
A: 

I prefer to use callback methods to signal the UI thread when something's done or progress needs to be updated. You can pass complex objects and the callback can return a value in case it needs to signal the worker thread. And you're allowed to have multiple callbacks defined depending upon how chatty you need your workers to be.

ebpower