C# 嵌套异步任务
我想知道是否有可能改进此代码以获得更好的性能。我不熟悉服务器端的整个异步工作,所以请在这里耐心听我说:C# 嵌套异步任务,c#,asp.net,task-parallel-library,C#,Asp.net,Task Parallel Library,我想知道是否有可能改进此代码以获得更好的性能。我不熟悉服务器端的整个异步工作,所以请在这里耐心听我说: 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 /
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()代码>阻止主线程
有什么方法可以使任务同时运行吗?考虑在GetGame页面中使用,而不是在Task中使用。继续使用理想情况下,您可以使这些操作一直异步:
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
});
没有LINQ的替代解决方案(尽管我喜欢Reed Copsey的解决方案)。但是,请注意,正如注释中所指出的,此解决方案通过将对GetPlayerDetailsAsync()
的调用封装在由Task.Run()
创建的任务中而引入了开销
需要.NET 4.5和C#5
con.GetGame(id,game=>{
var tasks=新列表();
foreach(游戏中的玩家p.Team1)
{
tasks.Add(Task.Run(async()=>p.SomeExtraDetails=wait-getPlayerDetails=wait-getPlayerDetailsSync(p.Id));
}
Task.WaitAll(tasks.ToArray());
});
专用任务GetPlayerDetailsAsync(双playerId)
{
返回con.GetPlayerAsync(playerId);
});
此外,为了在.NET 4.5中赶上基于任务的异步模式(TAP),我强烈建议阅读:由微软的Stephen Toub编写。您能使用C#5.0吗?另外,为什么您的一些方法使用continuations和一些Task
s?我认为如果你保持一致会更好。@svick我使用的是VS2012,但我相信它是.NET 4.5 atm。@Johan这不是打字错误-它是C#5(语言)和.NET 4.5(框架);)你介意告诉我它是如何在我的例子中实现的吗?@ Johan考虑其他答案并尽量避免复制粘贴。programming@IlyaBursov这与复制粘贴编程无关。这是关于理解您的确切意思以及如何实际使用Parallel.ForEach()
.Parallel.ForEach(game.Team1,p=>{p.SomeExtraDetails=GetPlayerDetails(p.Id);})上面答案中的复制粘贴`非常感谢您的详细回答。我尝试了上一个示例,但在返回任务时遇到异常。结果:当另一个写入操作挂起时,无法调用Write方法。“
@Johan听起来你的GetPlayer方法不是线程安全的;)@Johan在不知道发生了什么的情况下很难判断-顺便说一句,如果你能让它工作的话,上面的第一个选项将是非常好的(完全没有阻塞)好的,我已经尝试了第一个。await关键字通过突出显示,'await'运算符只能用于标记有'async'修饰符的方法或lambda
@Johan您需要将您的方法设置为异步方法。这很有效,看起来很不错,但不必要地使用线程池。。。Run将依赖TP线程来运行任务,而我的版本避免了这一点that@ReedCopsey我完全同意这一点。我应该在写答案的时候指出这一点。但是,我现在已经更新了我的答案,因此很明显,这个替代实现在多次调用Task.Run()
时会带来开销。
// 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);
});
});
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);
});