for (int i = 1; i < servers.Count;i++){
var server = new SpeedTestServer(servers[i]);
server.dist = haversine(server);
if (closestKnownServer.dist - server.dist > distTreshold){
closestKnownServer = server;
this.servers.Add(server);
this.servers.RemoveAt(0);
}
else if (Math.Abs(closestKnownServer.dist - server.dist) <= distTreshold){
this.servers.Add(server);
//BUG: we need to enable it but it causes hang
pingTasks.Add(
Task.Factory.StartNew(async () => {
await server.ping();
if (closestKnownServer.latency > server.latency){
closestKnownServer = server;
this.servers.RemoveAt(0);
}
})
);
}
}
await Task.WhenAll(pingTasks);
return closestKnownServer;
查看上面的代码。 我们创建一个任务列表并填充它。 然后我们等着他们。 但它不能正常工作! 使用WhenAll生成的任务如果完成,但它包含的任务不是。 如果将断点放入lambda并放入方法的最后一行,则可以看到这一点。
P.S。我知道我还需要同步,但是没有在c#中找到它的内置库。
答案 0 :(得分:3)
正如Svick评论的那样,Task.Factory.StartNew
具有与Task.Run
不同的重载和功能,这些差异已在此处发布:Task.Run vs Task.Factory.StartNew
。 Task.Factory.StartNew(async () => ...
的结果是Task<Task>
,因此在致电Task.WhenAll
时,您只是在等待外部Task
完成。
在上面链接的博客文章的最后,突出显示了async和await之间的差异。您有三种解决方案可以解决您的问题:
使用具有隐式异步支持的Task.Run
,因此将返回Task
以执行异步委托。
pingTasks.Add(
Task.Run(async () => {
await server.ping();
if (closestKnownServer.latency > server.latency){
closestKnownServer = server;
this.servers.RemoveAt(0);
}
})
);
使用Unwrap(Task<Task>)
扩展方法将Task<Task>
转换为Task
,在使用Task.Factory.StartNew
的更高级重载时,它正是为此目的而设计的。 p>
pingTasks.Add(
Task.Factory.StartNew(async () => {
await server.ping();
if (closestKnownServer.latency > server.latency){
closestKnownServer = server;
this.servers.RemoveAt(0);
}
}).Unwrap()
);
等待Task.Factory.StartNew(async () => ...)
的结果,它等待外部任务完成,并产生一个Task
,用于执行异步委托。
pingTasks.Add(
await Task.Factory.StartNew(async () => {
await server.ping();
if (closestKnownServer.latency > server.latency){
closestKnownServer = server;
this.servers.RemoveAt(0);
}
})
);
在所有这些中,根据您的情况,由于其隐式异步支持,我建议使用Task.Run
。