Подтвердить что ты не робот

Использование async/wait или task в контроллере web api (ядро .net)

У меня есть .net core API, у которого есть контроллер, который строит агрегированный объект для возврата. создаваемый объект создается из данных, поступающих из трех вызовов метода в класс службы. Все они независимы друг от друга и могут выполняться изолированно друг от друга. В настоящее время я использую задачи для повышения производительности этого контроллера. текущая версия выглядит примерно так:

[HttpGet]
public IActionResult myControllerAction()
{      
    var data1 = new sometype1();
    var data2 = new sometype2();
    var data3 = new List<sometype3>();

    var t1 = new Task(() => { data1 = service.getdata1(); });
    t1.Start();

    var t2 = new Task(() => { data2 = service.getdata2(); });
    t2.Start();

    var t3 = new Task(() => { data3 = service.getdata2(); });
    t3.Start();

    Task.WaitAll(t1, t2, t3);

    var data = new returnObject
    {
         d1 = data1,
         d2 = data2,
         d2 = data3
    };

    return Ok(data);
}

Это хорошо работает, но мне интересно, является ли использование задач лучшим решением здесь? Будет ли использование async/await лучшей идеей и более приемлемым способом?

Например, если контроллер должен быть помечен как асинхронный и ждать каждого вызова методам службы?

4b9b3361

Ответ 1

Это хорошо работает, но мне интересно, является ли использование задач лучшим решением здесь? Будет ли использование async/await лучшей идеей и более приемлемым способом?

Да, абсолютно. Выполнение параллельной обработки в ASP.NET потребляет несколько потоков для каждого запроса, что может серьезно повлиять на вашу масштабируемость. Асинхронная обработка намного выше для ввода-вывода.

Чтобы использовать async, сначала начните с вашего вызова самого низкого уровня, где-нибудь внутри вашего сервиса. Вероятно, он делает HTTP-вызов в какой-то момент; измените это на использование асинхронных HTTP-вызовов (например, HttpClient). Тогда пусть async естественно растет оттуда.

В итоге вы получите асинхронные методы getdata1Async, getdata2Async и getdata3Async, которые могут быть использованы одновременно:

[HttpGet]
public async Task<IActionResult> myControllerAction()
{
  var t1 = service.getdata1Async();
  var t2 = service.getdata2Async();
  var t3 = service.getdata3Async();
  await Task.WhenAll(t1, t2, t3);

  var data = new returnObject
  {
    d1 = await t1,
    d2 = await t2,
    d3 = await t3
  };

  return Ok(data);
}

При таком подходе, когда выполняются три вызова службы, myControllerAction использует ноль, а не четыре.

Ответ 2

[HttpGet]
public async Task<IActionResult> GetAsync()
{      
    var t1 = Task.Run(() => service.getdata1());
    var t2 = Task.Run(() => service.getdata2());
    var t3 = Task.Run(() => service.getdata3());

    await Task.WhenAll(t1, t2, t3);

    var data = new returnObject
    {
        d1 = t1.Status == TaskStatus.RanToCompletion ? t1.Result : null,
        d2 = t2.Status == TaskStatus.RanToCompletion ? t2.Result : null,
        d3 = t3.Status == TaskStatus.RanToCompletion ? t3.Result : null
    };

   return Ok(data);
}
  • В настоящее время поток действий заблокирован, когда вы ждете задач. Используйте TaskWhenAll, чтобы вернуть ожидаемый объект Task. Таким образом, с помощью метода async вы можете ждать задач вместо блокировки потока.
  • Вместо создания локальных переменных и назначения их в задачах вы можете использовать Task<T> для возврата результатов требуемого типа.
  • Вместо создания и запуска задач используйте метод Task<TResult>.Run
  • Я рекомендую использовать соглашение для имен действий - если действие принимает запрос GET, оно должно начинаться с Get
  • Затем вы должны проверить, успешно ли выполнены задачи. Это делается путем проверки состояния задачи. В моем примере я использовал значения null для свойств возвращаемого объекта, если некоторые из задач не завершены успешно. Вы можете использовать другой подход - например, return, если некоторые задачи не удались.

Ответ 3

Как я понимаю, вы хотите, чтобы это выполнялось параллельно, поэтому я не думаю, что с вашим кодом что-то не так. Как сказал Габриэль, вы можете дождаться завершения задач.

[HttpGet]
public async IActionResult myControllerAction()
{      
  var data1 = new sometype1();
  var data2 = new sometype2();
  var data3 = new List<sometype3>();

  var t1 = Task.Run(() => { data1 = service.getdata1(); });
  var t2 = Task.Run(() => { data2 = service.getdata2(); });
  var t3 = Task.Run(() => { data3 = service.getdata3(); });

  await Task.WhenAll(t1, t2, t3); // otherwise a thread will be blocked here

  var data = new returnObject
  {
      d1 = data1,
      d2 = data2,
      d2 = data3
  };

 return Ok(data);
}

Вы также можете использовать результаты задач для сохранения некоторых строк кодов и сделать код "лучше" (см. комментарии):

[HttpGet]
public async IActionResult myControllerAction()
{      
  var t1 = Task.Run(() => service.getdata1() );
  var t2 = Task.Run(() => service.getdata2() );
  var t3 = Task.Run(() => service.getdata3() );

  await Task.WhenAll(t1, t2, t3); // otherwise a thread will be blocked here

  var data = new returnObject
  {
      d1 = t1.Result,
      d2 = t2.Result,
      d2 = t3.Result
  };

 return Ok(data);
}