Почему я должен создавать операции async WebAPI вместо синхронизации? - программирование
Подтвердить что ты не робот

Почему я должен создавать операции async WebAPI вместо синхронизации?

В создавшемся веб-API я создал следующую операцию:

// GET api/<controller>
[HttpGet]
[Route("pharmacies/{pharmacyId}/page/{page}/{filter?}")]
public CartTotalsDTO GetProductsWithHistory(Guid pharmacyId, int page, string filter = null ,[FromUri] bool refresh = false)
{
    return delegateHelper.GetProductsWithHistory(CustomerContext.Current.GetContactById(pharmacyId), refresh);
}

Вызов этого веб-сервиса выполняется через вызов Jquery Ajax следующим образом:

$.ajax({
      url: "/api/products/pharmacies/<%# Farmacia.PrimaryKeyId.Value.ToString() %>/page/" + vm.currentPage() + "/" + filter,
      type: "GET",
      dataType: "json",
      success: function (result) {
          vm.items([]);
          var data = result.Products;
          vm.totalUnits(result.TotalUnits);
      }          
  });

Я видел некоторых разработчиков, которые реализуют предыдущую операцию следующим образом:

// GET api/<controller>
[HttpGet]
[Route("pharmacies/{pharmacyId}/page/{page}/{filter?}")]
public async Task<CartTotalsDTO> GetProductsWithHistory(Guid pharmacyId, int page, string filter = null ,[FromUri] bool refresh = false)
{
    return await Task.Factory.StartNew(() => delegateHelper.GetProductsWithHistory(CustomerContext.Current.GetContactById(pharmacyId), refresh));
}

Надо сказать, что GetProductsWithHistory() - довольно долгая операция. Учитывая мою проблему и контекст, как будет работать асинхронная операция webAPI?

4b9b3361

Ответ 1

В вашем конкретном примере операция не является асинхронной вообще, так что вы выполняете асинхронную синхронизацию. Вы просто выпускаете один поток и блокируете другого. Нет причин для этого, потому что все потоки являются потоками потоков пулов (в отличие от приложения GUI).

В моем обсуждении "async over sync" я настоятельно предположил, что если у вас есть API, который внутренне реализуется синхронно, вы не должны выставлять асинхронный экземпляр, который просто обертывает синхронный метод в Task.Run.

Из Должен ли я выставлять синхронные обертки для асинхронных методов?

Однако при вызове WebAPI async, где есть фактическая асинхронная операция (обычно I/O) вместо блокировки потока, который сидит и ждет результата, поток возвращается в пул потоков и поэтому способен выполнять некоторые другие операция. Все это означает, что ваше приложение может делать больше с меньшими ресурсами и улучшает масштабируемость.

Ответ 2

Один подход может быть (я использовал это успешно в клиентских приложениях), чтобы служба Windows выполняла длительные операции с рабочими потоками, а затем делала это в IIS, чтобы освободить потоки до завершения операции блокировки: Обратите внимание, что это предполагает, что результаты хранятся в таблице (строки, идентифицированные jobId), а более чистый процесс очищает их через несколько часов после использования.

Чтобы ответить на вопрос: "Учитывая мою проблему и контекст, как будет работать асинхронная операция webAPI?" учитывая, что это "довольно продолжительная операция", я думаю много секунд, а не мс, этот подход освобождает потоки IIS. Очевидно, вам также нужно запустить службу Windows, которая сама берет ресурс, но этот подход может помешать потоку медленных запросов от кражи потоков из других частей системы.

// GET api/<controller>
[HttpGet]
[Route("pharmacies/{pharmacyId}/page/{page}/{filter?}")]
public async Task<CartTotalsDTO> GetProductsWithHistory(Guid pharmacyId, int page, string filter = null ,[FromUri] bool refresh = false)
{
        var jobID = Guid.NewGuid().ToString()
        var job = new Job
        {
            Id = jobId,
            jobType = "GetProductsWithHistory",
            pharmacyId = pharmacyId,
            page = page,
            filter = filter,
            Created = DateTime.UtcNow,
            Started = null,
            Finished = null,
            User =  {{extract user id in the normal way}}
        };
        jobService.CreateJob(job);

        var timeout = 10*60*1000; //10 minutes
        Stopwatch sw = new Stopwatch();
        sw.Start();
        bool responseReceived = false;
        do
        {
            //wait for the windows service to process the job and build the results in the results table
            if (jobService.GetJob(jobId).Finished == null)
            {
                if (sw.ElapsedMilliseconds > timeout ) throw new TimeoutException();
                await Task.Delay(2000);
            }
            else
            {
                responseReceived = true;
            }
        } while (responseReceived == false);

    //this fetches the results from the temporary results table
    return jobService.GetProductsWithHistory(jobId);
}