Лучшая производительность с использованием асинхронных и ожидающих

У меня есть одноядерный компьютер, и я хотел бы улучшить производительность своего кода, используя async и await. Код состоит из двух основных частей. первая операция ввода-вывода. (чтение из очереди служебной шины Azure для нескольких sessionId), второе, обработка данных - загрузка процессора. Я обернул метод DequeueAsync в асинхронный метод, который возвращает задачу:

private async Task<SomeReturnValue> AcceptFromQueueAsync(){
  try{ 
       SomeReturnValue result = await DequeueAsync.configureAwait(false);
       return SomeReturnValue;  
  }
  catch{
   //logging and stuff
  }

Тяжелым методом для CPU является sync: DoSyncWork () Поскольку вторая часть является тяжелой для CPU, я не хочу использовать параллелизм (на самом деле не могу...). ЦП может начинаться только после завершения IO-части. Учитывая вышеизложенное, является ли следующая реализация способом использования 1 процессора?

private void AcceptAndProcessWrapper(){
    //Count is some cosnt defined outside the method
    _acceptTasks = new List<Task<SomeReturnValue>>();
     for (var i = 0; i < Count; i++)
     {
                    _acceptTasks.Add(AcceptFromQueueAsync());
      }
      //The use of list is for convince in processing  
      Task.WaitAll(_acceptTasks.ToArray());
      //The CPU part 
       Task.Run(DoSyncWork).Wait();
 }

Я знаю, что мог бы не использовать Task.Run() для части синхронизации, но я хочу разрешить реализацию функций на нескольких ядрах (запустив несколько задач с помощью Task.Run() и удерживая их в массиве) Будет ли приведенная выше реализация, (Несколько вызовов асинхронного метода, который возвращает задачу) улучшить производительность? Или я должен начать новую задачу для каждого асинхронного вызова, например Task.Run(AcceptFromQueueAsync)?

4 ответа

Решение

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

Добавление дополнительного звонка Task.Run не поможет Запуск асинхронной задачи в другом потоке - это просто добавление дополнительных издержек без добавленной стоимости. Task.Run следует использовать для запуска связанной с ЦП работы в другом потоке, а не асинхронной работы в другом потоке.

Говоря о производительности с заданием (ями) (если вы будете использовать его в будущем с DoSyncWork Вы должны также иметь в виду, что Task.Run по умолчанию использует ThreadPool, но ожидается, что потоки из пула потоков будут выполнять небольшую и быструю работу. Если задачи действительно являются "тяжелыми", вам следует рассмотреть возможность их запуска во вновь созданных потоках, что также может быть вызвано следующим:

new Task(() => { }, TaskCreationOptions.LongRunning);

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

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

В настоящее время, Task.Run(DoSyncWork).Wait(); на самом деле не имеет никакого смысла (даже больше, это может занять дополнительное время для запуска задачи в другом потоке (даже из потоков ThreadPool), но если вы хотите использовать несколько ядер в будущем, вам, очевидно, следует переместить эту задачу в другой поток, чтобы запустите (и если он действительно сильно загружен процессором, подумайте о запуске задачи как " LongRunning ") (если это, конечно, необходимо для вашего приложения (не знаю, какой у вас тип приложения)). В этом случае все еще нет нужно позвонить .Wait() там мгновенно.

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

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

public async void DoWork()
{
    Task cpuBoundTask = null;
    Task ioBoundTask = null;

    while(true)
    {
         ioBoundTask = DoIOBoundStuff();
         var ioResult = await ioBoundTask;

         if(cpuBoundTask != null)
         {
             await cpuBoundTask;
         }
         cpuBoundTask = DoCpuBoundStuff(ioResult);
    }
 }
Другие вопросы по тегам