Asyn c Реализация очереди .Wait () быстрее, чем await - PullRequest
0 голосов
/ 08 мая 2020

Чтобы обеспечить функциональность производителя-потребителя, которая может ставить в очередь и выполнять asyn c методы один за другим, я пытаюсь реализовать очередь asyn c. Я заметил серьезные проблемы с производительностью, используя его в большом приложении.

async Task Loop() {
     while (Verify()) {
         if (!_blockingCollection.TryTake(out var func, 1000, _token)) continue;
         await func.Invoke();       
     }
}

Реализация AsyncQueue.Add:

public void Add(Func<Task> func) {
    _blockingCollection.Add(func);
}

Пример использования из произвольного потока:

controller.OnEvent += (o, a) => _queue.Add(async (t) => await handle(a));

Пути выполнения зависят от состояния приложения и включают

  • asyn c сетевые запросы, которые внутренне используют TaskCompletionSource для возврата результата

  • Операции ввода-вывода

  • задачи, которые добавляются в список и ожидаются с помощью Task.WhenAll (...)

  • asyn c void метод, который преобразует массив и ожидает сетевого запроса

Симптомы: приложение постепенно замедляется.

Когда я заменяю await func.Invoke() на func.Invoke().Wait() вместо ожидания это правильно, производительность резко улучшается и не тормозит.

Почему так? Является ли очередь asyn c, использующая BlockingCollection, плохой идеей?

Какая альтернатива лучше?

1 Ответ

3 голосов
/ 08 мая 2020

Почему это?

В вопросе недостаточно информации, чтобы дать ответ на него.

Как уже отмечали другие, есть CPU- потребляющая проблема вращения с l oop, как сейчас.

А пока я могу хотя бы ответить на эту часть:

Это асинхронная c Очередь, которая использует BlockingCollection - плохая идея?

Да.

Какая альтернатива лучше?

Используйте асинхронную c -совместимую очередь. Например, Channels или BufferBlock / ActionBlock из потока данных TPL.

Пример использования каналов:

async Task Loop() {
  await foreach (var func in channelReader.ReadAllAsync()) {
    await func.Invoke();
  }
}

или, если вы не подключены. NET Ядро пока :

async Task Loop() {
  while (await channelReader.WaitToReadAsync()) {
    while (channelReader.TryRead(out var func)) {
      await func.Invoke();
    }
  }
}
...