Почему метод асинхронного контроллера выполняется так же, как метод синхронизации? - PullRequest
0 голосов
/ 24 января 2019

Я создал образец приложения .NET Core WebApi для проверки того, как асинхронные методы могут увеличить пропускную способность.Приложение размещено на IIS 10.

Вот код моего контроллера:

[HttpGet("sync")]
public IEnumerable<string> Get()
{
    return this.GetValues().Result;
}

[HttpGet("async")]
public async Task<IEnumerable<string>> GetAsync()
{
    return await this.GetValues();
}

[HttpGet("isresponding")]
public Task<bool> IsResponding()
{
    return Task.FromResult(true);
}

private async Task<IEnumerable<string>> GetValues()
{
    await Task.Delay(TimeSpan.FromSeconds(10)).ConfigureAwait(false);
    return new string[] { "value1", "value2" };
}

есть методы: Get() - получить результат синхронно GetAsync() - получить результат асинхронно,IsResponding() - чтобы проверить, что сервер может обслуживать запросы

Затем я создал пример консольного приложения, которое создает 100 запросов на синхронизацию и асинхронный метод (без ожидания результата) контроллера.Затем я вызываю метод IsResponding(), чтобы проверить, доступен ли сервер.

Код консольного приложения:

      using (var httpClient = new HttpClient())
      {
        var methodUrl = $"http://localhost:50001/api/values/{apiMethod}";
        Console.WriteLine($"Executing {methodUrl}");
        //var result1 = httpClient.GetAsync($"http://localhost:50001/api/values/{apiMethod}").Result.Content.ReadAsStringAsync().Result;

        Parallel.For(0, 100, ((i, state) =>
        {
          httpClient.GetAsync(methodUrl);
        }));

        var sw = Stopwatch.StartNew();
        var isAlive = httpClient.GetAsync($"http://localhost:50001/api/values/isresponding").Result.Content;
        Console.WriteLine($"{sw.Elapsed.TotalSeconds} sec.");

        Console.ReadKey();
      }

, где {apiMethod} - это «синхронизация» или «асинхронность», в зависимости от ввода пользователя.

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

ОБНОВЛЕНИЕ 1: Я изменил код клиента следующим образом:

  Parallel.For(0, 10000, ((i, state) =>
  {
    var httpClient = new HttpClient();
    httpClient.GetAsync($"http://localhost:50001/api/values/{apiMethod}");
  }));

  using (var httpClient = new HttpClient())
  {
    var sw = Stopwatch.StartNew();
    // this method should evaluate fast when we called async version and should evaluate slowly when we called sync method (due to busy threads ThreadPool)
    var isAlive = httpClient.GetAsync($"http://localhost:50001/api/values/isresponding").Result.Content;
    Console.WriteLine($"{sw.Elapsed.TotalSeconds} sec.");
  }

и вызов метода IsResponding(), выполняющегося в течение очень долгого времени.

ОБНОВЛЕНИЕ 2 Да, я знаю, как работают асинхронные методы.Да, я знаю, как использовать HttpClient.Это всего лишь пример для подтверждения теории.

ОБНОВЛЕНИЕ 3 Как было упомянуто StuartLC в одном из комментариев, IIS как-то ограничивает или блокирует запросы.Когда я запустил свой WebApi как SelfHosted, он начал работать, как и ожидалось:

  1. Время выполнения метода "isresponsible" после большого количества запросов к методу ASYNC очень быстрое, около 0,02 сек .
  2. Время выполнения «ответственного» метода после большого количества запросов к методу SYNC очень медленное, около 35 сек .

Ответы [ 3 ]

0 голосов
/ 25 января 2019

Я не уверен, что это приведет к каким-либо существенным улучшениям, но вы должны вызывать ConfigureAwait(false) на каждом ожидаемом сервере, в том числе GetAsync.

. Это должно обеспечить лучшее использование потоков.

0 голосов
/ 25 января 2019

IIS как-то ограничивает или блокирует запросы (как упомянуто в одном из комментариев).Когда я запустил свой WebApi как SelfHosted, он начал работать, как и ожидалось:

Время выполнения метода isresponsible после большого количества запросов к методу ASYNC очень быстрое, около 0,02 сек.Время выполнения метода isresponsible после пакета запросов к методу SYNC очень медленное, около 35 секунд.

0 голосов
/ 24 января 2019

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

То, что асинхронная делает , потенциальноразрешить возврат активного потока, обслуживающего запрос, в пул для обслуживания других запросов.Другими словами, асинхронность - это масштаб, а не производительность.Вы увидите преимущества только тогда, когда ваш сервер забит запросами.Затем, когда входящие запросы обычно помещаются в очередь синхронизации, вы обрабатываете дополнительные запросы от некоторых асинхронных задач, теряя свои потоки по причине.Кроме того, нет никакой гарантии, что поток будет вообще освобожден.Если асинхронная задача завершается немедленно или почти сразу, поток будет удерживаться, как при синхронизации.

РЕДАКТИРОВАТЬ

Следует также понимать, что IIS Express является однорезьба.Таким образом, это не очень хорошая манера для настройки производительности.Если вы выполняете 1000 одновременных запросов, 999 мгновенно ставятся в очередь.Тогда вы не выполняете никакой асинхронной работы - просто возвращаете выполненную задачу.Таким образом, поток никогда не будет освобожден, поэтому в этом сценарии нет никакой разницы между синхронизацией и асинхронностью.Таким образом, вы дошли до того, сколько времени потребуется для обработки через очередь из 999 запросов (плюс проверка вашего состояния в конце).Возможно, вам повезет больше узнать разницу, если вы сделаете что-то вроде:

await Task.Delay(500);

Вместо того, чтобы просто вернуть Task.FromResult.Таким образом, в потоке есть фактическое время простоя, которое может позволить его вернуть в пул.

...