使用异步等待进行多个服务调用的最佳方式是什么

作为异步和等待最佳实践的一部分,建议不要使用 Task.Run。我有一个服务,它对第三方服务进行多次调用,我们使用异步来进行这些调用。我正在寻找有关以下代码改进的建议。


  public interface IRouteService

{

     Task<IEnumerable<Route>> GetRoute(Coordinates orign, Coordinates destination);

}



public class RouteProvider

{

    private readonly IRouteService _routeService; 


    public RouteProvider(IRouteService routeService)

    {

        _routeService = routeService;

    }


    public async Task<IEnumerable<Route>> GetRoutes(IEnumerable<Coordinates> origns, IEnumerable<Coordinates> destinations)


    {

        ConcurrentBag<Route> routes = new ConcurrentBag<Route>();

        List<Task> tasks = new List<Task>();

        foreach (var origin in origns)

        {

            foreach (var destination in destinations)

            {

                tasks.Add(Task.Run(async () =>

                {

                  var response=  await _routeService.GetRoute(origin, destination);

                    foreach (var item in response)

                    {

                        routes.Add(item);

                    }

                }));

            }

        }

        Task.WaitAll(tasks.ToArray());

        return routes;

    }


}



public class Route

{

    public string Distance { get; set; }


    public Coordinates Origin { get; set; }


    public object Destination { get; set; }


    public string OriginName { get; set; }


    public string DestinationName { get; set; }


}


public class  Coordinates

{

    public float Lat { get; set; }


    public float Long { get; set; }



}


哆啦的时光机
浏览 221回答 3
3回答

慕妹3146593

对于这样的问题,使用 LINQ 很方便。LINQ 生成不可变的结果,因此您可以避免并发问题,也不需要任何专用集合。一般来说,使用LINQ或类似的编程技术(即像函数式程序员一样思考)将使多线程更容易。public async Task<IEnumerable<Route>> GetRoutes(IEnumerable<Coordinates> origins, IEnumerable<Coordinates> destinations){&nbsp; &nbsp; var tasks = origins&nbsp; &nbsp; &nbsp; &nbsp; .SelectMany&nbsp; &nbsp; &nbsp; &nbsp; (&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; o => destinations.Select&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; d => _routeService.GetRoute(o, d)&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; )&nbsp; &nbsp; &nbsp; &nbsp; );&nbsp; &nbsp; await Task.WhenAll( tasks.ToArray() );&nbsp; &nbsp; return tasks.SelectMany( task => task.Result );}

忽然笑

正如评论中指出的那样,我建议您可以使用 来确定要完成的所有任务并获得结果。为此,您可以更新代码,如下所示。Task.WhenAll()return await Task.WhenAll(tasks);public async Task<IEnumerable<Route>> GetRoutes(IEnumerable<Coordinates> origns, IEnumerable<Coordinates> destinations){&nbsp; &nbsp; ConcurrentBag<Route> routes = new ConcurrentBag<Route>();&nbsp; &nbsp; List<Task> tasks = new List<Task>();&nbsp; &nbsp; foreach (var origin in origns)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; foreach (var destination in destinations)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tasks.Add(_routeService.GetRoute(origin, destination));&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; var response = await Task.WhenAll(tasks);&nbsp; &nbsp; foreach (var item in response)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; routes.Add(item);&nbsp; &nbsp; }&nbsp; &nbsp; return routes;}}由于所有调用都将返回相同的类型,因此无需在其他循环中启动第二个调用。此外,通过这种方式,您将避免锁定线程执行,并且您的程序将运行得更加同步。要查看 与 之间的区别,您可以查看此内容。foreachTask.WaitAll()WhenAll()WaitAll()

慕沐林林

您可以使用延续,而不是使用该方法直接创建任务。Task.Runforeach (var origin in origns){&nbsp; &nbsp; foreach (var destination in destinations)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; tasks.Add(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _routeService.GetRoute(origin, destination)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .ContinueWith(response =>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; foreach (var item in response.Result)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; routes.Add(item);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; })&nbsp; &nbsp; &nbsp; &nbsp; );&nbsp; &nbsp; }}因此,该方法将异步执行,而无需创建单独的线程。从中获得的结果将在单独的线程(任务)中处理。GetRoute但是,仅当结果需要很长时间才能处理时,才需要这样做。否则,根本不需要单独的线程。
打开App,查看更多内容
随时随地看视频慕课网APP