Johan
Johan

Reputation: 35194

Nested asynchronous tasks

I would like to know if it's possible to improve this code for better performance. I'm new to the whole async thing on the serverside, so please bear with me here:

con.GetGame(id, game => {

    foreach(Player p in game.Team1)
    {
        p.SomeExtraDetails = GetPlayerDetails(p.Id);
    }

    // I would like the player data to be set on all players
    // before ending up here
});

private PlayerDetails GetPlayerDetails(double playerId)
{
    var task = con.GetPlayer(playerId);

    PlayerDetails ret = null;

    Task continuation = task.ContinueWith(t =>
    {
        ret = t.Result;
    });

    continuation.Wait();

    return ret;
}

If I got it right, continuation.Wait(); blocks the main thread.

Is there any way to make the tasks run simultaneously?

Upvotes: 3

Views: 3081

Answers (3)

Lasse Christiansen
Lasse Christiansen

Reputation: 10325

Alternative solution without LINQ (although I like Reed Copsey's solution). However, beware that, as pointed out in the comments, this solution introduces an overhead by encapsulating the call to GetPlayerDetailsAsync() inside Tasks created by Task.Run().

Requires .NET 4.5 and C# 5.

con.GetGame(id, game => {

    var tasks = new List<Task>();

    foreach(Player p in game.Team1)
    {
        tasks.Add(Task.Run(async () => p.SomeExtraDetails = await GetPlayerDetailsAsync(p.Id)));
    }

    Task.WaitAll(tasks.ToArray());
});

private Task<PlayerDetails> GetPlayerDetailsAsync(double playerId)
{
    return con.GetPlayerAsync(playerId);
});

Further, in order to catch up on the Task-based Asynchronous Pattern (TAP) with .NET 4.5 I highly recommend reading: Task-based Asynchronous Pattern - by Stephen Toub, Microsoft.

Upvotes: 0

Reed Copsey
Reed Copsey

Reputation: 564323

Ideally, you'd make these operations asynchronous all the way down:

private Task<PlayerDetails> GetPlayerDetailsAsync(double playerId)
{
    return con.GetPlayer(playerId);
}

con.GetGame(id, game => {
    var tasks = game.Team1
                    .Select(p => new { Player=p, Details=GetPlayerDetailsAsync(p.Id)})
                    .ToList(); // Force all tasks to start...

    foreach(var t in tasks)
    {
        t.Player.SomeExtraDetails = await t.Details;
    }

    // all player data is now set on all players
});

If that isn't an option (ie: you're not using VS 2012), you could simplify your code to:

// This is a more efficient version of your existing code
private PlayerDetails GetPlayerDetails(double playerId)
{
    var task = con.GetPlayer(playerId);
    return task.Result;
}

con.GetGame(id, game => {
    // This will run all at once, but block until they're done
    Parallel.ForEach(game.Team1, p =>
    {
        p.SomeExtraDetails = GetPlayerDetails(p.Id);
    });

});

Upvotes: 7

Iłya Bursov
Iłya Bursov

Reputation: 24145

consider using Parallel.ForEach in your GetGame page instead of Task.ContinueWith

Upvotes: 0

Related Questions