异步队列实现.Wait()比等待快



为了提供生产者-消费者功能,使其能够一个接一个地排队和执行async方法,我正在尝试实现一个异步队列。在大型应用程序中使用它时,我注意到了主要的性能问题。

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));

执行路径取决于应用程序的状态,包括

  • 内部使用TaskCompletionSource返回结果的异步网络请求

  • IO操作

  • 添加到列表中并使用任务等待的任务。WhenAll(…(

  • 一种异步void方法,用于转换数组并等待网络请求

症状:应用程序逐渐变慢。

当我用func.Invoke().Wait()替换await func.Invoke()而不是正确地等待它时,性能会显著提高,而且速度不会减慢。

为什么?使用BlockingCollection的异步队列是个坏主意吗?

什么是更好的选择?

为什么?

问题中没有足够的信息来提供答案。

正如其他人所指出的,循环目前存在消耗CPU的旋转问题

与此同时,我至少可以回答这一部分:

使用BlockingCollection的异步队列是个坏主意吗?

是。

什么是更好的替代方案?

使用异步兼容队列。例如,来自TPL数据流的ChannelsBufferBlock/ActionBlock

使用通道的示例:

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

或者如果你还没有使用.NET Core:

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

最新更新