Kevin M.
Kevin M.

Reputation: 21

Whether to convert from Task<>.Factory.StartNew / Task.Factory.ContinueWhenAll() to async/await?

I have the simplified code below that gets shipping rates from multiple carriers asynchronously and was wondering if it would be worthwhile to convert to using an async/await methodology instead and if so what the best approach would be to go about doing that? Or if its working okay now is it really not worth the effort? Thank you.

List<Task<ShippingRateCollection>> lstTasks = new List<Task<ShippingRateCollection>>();
Task<ShippingRateCollection> t;

t = Task<ShippingRateCollection>.Factory.StartNew(() => { return GetUPSRates(...); });
lstTasks.Add(t);

t = Task<ShippingRateCollection>.Factory.StartNew(() => { return GetUSPSRates(...); });
lstTasks.Add(t);

t = Task<ShippingRateCollection>.Factory.StartNew(() => { return GetFedExRates(...); });
lstTasks.Add(t);

//wait until all requests complete (or error out)
Task.Factory.ContinueWhenAll(lstTasks.ToArray(), (tasks) =>
{
    //wrap all exceptions into 1 AggregateException
    Task.WaitAll(tasks);
});

foreach (Task<ShippingRateCollection> task in lstTasks)
{
    foreach (ShippingRate rate in task.Result)
    {
        ... //display rate
    }   //next returned rate
}

Upvotes: 1

Views: 182

Answers (3)

Paulo Morgado
Paulo Morgado

Reputation: 14846

A less verbose version of Jakub Lortz's answer:

var results = await Task.WhenAll(
    GetUPSRatesAsync(),
    GetUSPSRatesAsync(),
    GetFedExRatesAsync());

Upvotes: 0

Enigmativity
Enigmativity

Reputation: 117064

I would look at using Microsoft's Reactive Framework for this. You can use this code:

var fs = new Func<ShippingRateCollection>[]
{
    () => GetUPSRates(...),
    () => GetUSPSRates(...),
    () => GetFedExRates(...),
};

var query =
    from f in fs.ToObservable()
    from rate in Observable.Start(f)
    select rate;

query
    .Subscribe(rate =>
    {
        //display rate
    });

The code runs asynchronously and the .Subscribe(...) method will return results as soon as they are available rather than waiting for all of them to finish.

If you do want them to finish then you can change the code like this:

query
    .ToArray()
    .Subscribe(rates =>
    {
        foreach (ShippingRate rate in rates)
        {
            //display rate
        }
    });

Just NuGet "Rx-Main" to get the required bits.

Upvotes: 1

Jakub Lortz
Jakub Lortz

Reputation: 14896

You should definitely refactor it to async/await if you can make the Get*Rates methods asynchronous. In your current code you execute them on separate threads only to block the threads waiting for I/O to complete. That's a waste.

If you can make these methods asynchronous, the code might look like this:

var tasks = new Task<ShippingRateCollection>[]
{
    GetUPSRatesAsync(),
    GetUSPSRatesAsync(),
    GetFedExRatesAsync()
};

ShippingRateCollection[] results = await Task.WhenAll(tasks);

// process results

Even if you have to work with synchronous GetRates methods, refactoring the code to async/await will simplify it.

Upvotes: 2

Related Questions