任务。当所有包含的任务完成之前,它们已完成

时间:2013-09-18 13:05:11

标签: c# task-parallel-library async-await

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并放入方法的最后一行,则可以看到这一点。

The full code

P.S。我知道我还需要同步,但是没有在c#中找到它的内置库。

1 个答案:

答案 0 :(得分:3)

正如Svick评论的那样,Task.Factory.StartNew具有与Task.Run不同的重载和功能,这些差异已在此处发布:Task.Run vs Task.Factory.StartNewTask.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