2013-10-18 90 views
3

我想知道是否有可能改进此代码以获得更好的性能。我是新来的服务器端全异步的东西,所以请容忍我在这里:嵌套异步任务

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; 
} 

如果我这样做是正确,continuation.Wait();块主线程。

有什么办法可以让任务同时运行吗?

+0

你可以使用C#5.0吗?另外,为什么你的一些方法使用continuations和一些'Task's?我认为如果你一致的话会更好。 – svick

+0

@svick我正在使用VS2012,但我相信它是.NET 4.5 atm。 – Johan

+1

@Johan它不是一个错字 - 它是C#5(语言)和.NET 4.5(框架);) –

回答

6

理想情况下,你会做这些操作异步一路下滑:

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 
}); 

如果这不是一个选项(即:你不使用VS 2012),可以简化您的代码:

// 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); 
    }); 

}); 
+0

非常感谢您的详细解答。我尝试了你的最后一个例子,但是我在'return task.Result'上得到了一个异常:'Write方法在另一个写操作挂起时不能被调用。'' – Johan

+1

@Johan这听起来像你的GetPlayer方法不是线程安全的; ) –

+0

我明白了。任何简单的方法来解决它,或者我运气不好?:) – Johan

0

考虑您的GetGame页面,而不是使用Task.ContinueWith Parallel.ForEach

+0

你介意在我的例子中展示它是如何实现的吗? – Johan

+0

@Johan考虑其他答案并尽量避免复制粘贴编程 –

+3

@IlyaBursov这不是复制粘贴编程。这是为了了解你的意思,以及你将如何使用'Parallel.ForEach()'。 – svick

0

替代解决方案,而LINQ(虽然我喜欢里德·科普塞的解决方案)。但是,要注意,正如评论中指出的那样,该解决方案通过在Task.Run()创建的任务中封装调用GetPlayerDetailsAsync()来引入开销。

需要.NET 4.5和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); 
}); 

此外,为了赶上在基于任务的异步模式(TAP)与.NET 4.5我高度推荐阅读:Task-based Asynchronous Pattern - 由Stephen Toub ,微软。

+2

这工作,看起来不错,但不必要地使用ThreadPool ...任务。运行将依靠TP线程来运行任务,我的版本避免 –

+0

@ReedCopsey我完全同意这一点。我在写答案的时候应该指出这一点。然而,我现在已经更新了我的答案,所以很明显这个替代实现引入了对Task.Run()的多个调用的开销。 –