为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

C#
哆啦的时光机 2022-08-20 17:45:35
作为异步和等待最佳实践的一部分,建议不要使用 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; }}
查看完整描述

3 回答

?
慕妹3146593

TA贡献1820条经验 获得超9个赞

对于这样的问题,使用 LINQ 很方便。LINQ 生成不可变的结果,因此您可以避免并发问题,也不需要任何专用集合。


一般来说,使用LINQ或类似的编程技术(即像函数式程序员一样思考)将使多线程更容易。


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

{

    var tasks = origins

        .SelectMany

        (

            o => destinations.Select

            ( 

                d => _routeService.GetRoute(o, d) 

            )

        );

    await Task.WhenAll( tasks.ToArray() );

    return tasks.SelectMany( task => task.Result );

}


查看完整回答
反对 回复 2022-08-20
?
忽然笑

TA贡献1806条经验 获得超5个赞

正如评论中指出的那样,我建议您可以使用 来确定要完成的所有任务并获得结果。为此,您可以更新代码,如下所示。Task.WhenAll()return await Task.WhenAll(tasks);


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(_routeService.GetRoute(origin, destination));

        }

    }


    var response = await Task.WhenAll(tasks);


    foreach (var item in response)

    {

        routes.Add(item);

    }

    return routes;

}

}


由于所有调用都将返回相同的类型,因此无需在其他循环中启动第二个调用。此外,通过这种方式,您将避免锁定线程执行,并且您的程序将运行得更加同步。要查看 与 之间的区别,您可以查看此内容。foreachTask.WaitAll()WhenAll()WaitAll()


查看完整回答
反对 回复 2022-08-20
?
慕沐林林

TA贡献2016条经验 获得超9个赞

您可以使用延续,而不是使用该方法直接创建任务。Task.Run


foreach (var origin in origns)

{

    foreach (var destination in destinations)

    {

        tasks.Add(

            _routeService.GetRoute(origin, destination)

                .ContinueWith(response =>

                {

                    foreach (var item in response.Result)

                        routes.Add(item);

                })

        );

    }

}

因此,该方法将异步执行,而无需创建单独的线程。从中获得的结果将在单独的线程(任务)中处理。GetRoute


但是,仅当结果需要很长时间才能处理时,才需要这样做。否则,根本不需要单独的线程。


查看完整回答
反对 回复 2022-08-20
  • 3 回答
  • 0 关注
  • 170 浏览

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号