views:

214

answers:

3

I am looking into using either of mentioned ways of dealing with long running operations.

I won't be going into details what can be accomplished using each of these, but I'd rather explain what I want to do.

I have a set of HTTP-related methods, each retrieving a few pieces of information. This is all part of bigger system inside of a long-running service (so it's not some do-once, throw-away code).

Generally, which approach is better, encapsulation and maintainability wise: handling returned data when it's ready using Begin/End/AsyncCallback mechanism, or by firing events from within Tasks, like OnXXXReceived, OnXXXError, using derived EventArgs classes for each event?

I hope I was being clear enough.. I decided not to supply code samples, since I'm more interested in opinions, advices and possible pitfalls, than in tidbits of specific implementation.

Thanks!

+2  A: 

In general the TPL Tasks are much easier to use. In your case, create them using a Factory and LongRunning option.

I'm not so sure about the events though. Why not process the Request/Rresponse in a loop or something similar? You're already on a Thread.

Henk Holterman
Mostly because these operations are, from encapsulation point of view, not the ones who know what should they do with data, once they retrieve it. It's outer scope who does.. But, actually, you might be right. I was thinking of putting Tasks into class that does HTTP methods, so from outer code I would subscribe to events and then invoke method that internally starts task which retrieves information, and fires correct event(s). Didn't think of putting tasks into outer scope, to handle returned data directly.
mr.b
+1  A: 

You could also use ContinuationTasks to process the data once the fetching task completes.

    Task<StockDataCollection> loadFedHistoricalData =
        Task<StockDataCollection>.Factory.StartNew(
            () => LoadFedHistoricalData(),
            TaskCreationOptions.LongRunning);

    Task<StockDataCollection> normalizeHistoricalData =
        loadFedHistoricalData.ContinueWith(
            (t) => NormalizeData(t.Result));

In this code the loadFedHistoricalData task runs LoadFedHistoricalData which is a long running I/O bound task. When this returns it continues with another task that normalizes the data.

ContinueWith takes a TaskContinuationOptions parameter that can be uses to specify if the continuation task runs always or just if some condition like error or cancellation is met.

The Chapter 5 A-Dash example shows this sort of alternative approach to eventing. You can download the code from http://parallelpatterns.codeplex.com/. Look at the AnalysisEngine and MainWindowViewModel classes.

Ade Miller
Good idea, thanks.
mr.b
A: 

You simply cannot beat the Asynchronous Programming Model (APM) when it comes to I/O performance. Anytime you can use it, you should be. Luckily the Task Parallel Library (TPL) comes with baked in support for combining APM work into the mix with "pure" TPL tasks via the FromAsync factory method.

Check out this section of the .NET SDK on MSDN entitled TPL and Traditional .NET Asynchronous Programming for more information on how to combine these two programming models to achieve async nirvana.

Drew Marsh