Каков наилучший способ сделать несколько вызовов службы с асинхронным ожиданием - PullRequest
0 голосов
/ 03 февраля 2019

В качестве рекомендаций по асинхронности и ожиданию рекомендуется не использовать 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 ]

0 голосов
/ 03 февраля 2019

Для такой проблемы удобно использовать 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 );
}
0 голосов
/ 03 февраля 2019

Вместо непосредственного создания задач с использованием метода 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 будет выполняться асинхронно, без создания отдельного потока.И полученный из него результат будет обработан в отдельном потоке (задаче).

Однако это необходимо только в том случае, если результат занимает много времени для обработки.В противном случае отдельный поток вообще не нужен.

0 голосов
/ 03 февраля 2019

Как указано в комментариях, я бы предположил, что вы можете использовать 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;
}

}

Поскольку все вызовы будут возвращать один и тот же тип, вам не нужно начинать секунду foreachв другом цикле.Кроме того, таким образом вы избежите блокировки выполнения потока с помощью Task.WaitAll(), и ваша программа будет работать более синхронно.Чтобы увидеть разницу между WhenAll() против WaitAll(), вы можете проверить this out.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...