.NET: Mechanism for sync-ing long-running tasks
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:
- Send some progress information about the task (progress, activity logging etc.)
- 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开发者_JS百科 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.
That sounds a lot like Windows Workflow Foundation.
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.
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.
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 callingbw.ReportProgress
and check for pending cancellation usingbw.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 yourDoWork
method reports some progress change.
For simpler problems, I believe you could represent your tasks as background workers.
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.
精彩评论