gpt4 book ai didi

c# - 在 for 中使用 await Task.Delay 会降低性能

转载 作者:太空狗 更新时间:2023-10-29 22:10:37 25 4
gpt4 key购买 nike

假设我想开始大约每秒平均分配 N 个任务。

所以我试了一下:

public async Task Generate(int numberOfCallsPerSecond) 
{
var delay = TimeSpan.FromMiliseconds(1000/numberOfCallsPerSecond); // a call should happen every 1000 / numberOfCallsPerSecond miliseconds
for (int i=0; i < numberOfcallsPerSecond; i++)
{
Task t = Call(); // don't wait for result here
await Task.Delay(delay);
}
}

起初我预计它会在 1 秒内运行,但对于 numberOfCallsPerSecond = 100,在我的 12 核 CPU 上它需要 16 秒。似乎 await Task.Delay 增加了很多开销(当然,如果没有它,调用的生成将在 3 毫秒内发生。

没想到在这种场景下await会增加这么多的开销。这正常吗?

编辑:

请忘记 Call()。运行此代码会显示类似的结果:

public async Task Generate(int numberOfCallsPerSecond) 
{
var delay = TimeSpan.FromMiliseconds(1000/numberOfCallsPerSecond); // a call should happen every 1000 / numberOfCallsPerSecond miliseconds
for (int i=0; i < numberOfcallsPerSecond; i++)
{
await Task.Delay(delay);
}
}

我尝试使用 numberOfCallsPerSecond = 500 运行它,它大约需要 10 秒,我预计 Generate 大约需要 1 秒,而不是 10 倍多

最佳答案

Task.Delay 是轻量级但不准确。由于没有延迟的循环完成得更快,听起来您的线程正在空闲并使用操作系统 sleep 来等待计时器结束。定时器根据操作系统线程调度量程(在执行线程抢占的同一个中断处理程序中)进行检查,默认为16ms。

您可以使用 timeBeginPeriod 减少量程,但如果您需要速率限制而不是精确计时,更好(更节能)的方法是跟踪耗时(秒表 class 对此有好处)和调用次数,并且仅在调用 catch 耗时时才延迟。总体效果是您的线程每秒将唤醒约 60 次,并且每次启动一些工作项。如果您的 CPU 忙于处理其他事情,当您重新获得控制权时,您将启动额外的工作项目——尽管如果您需要的话,限制一次启动的项目数量也非常简单。

public async Task Generate(int numberOfCallsPerSecond) 
{
var elapsed = Stopwatch.StartNew();
var delay = TimeSpan.FromMilliseconds(1000/numberOfCallsPerSecond); // a call should happen every 1000 / numberOfCallsPerSecond milliseconds
for (int i=0; i < numberOfcallsPerSecond; i++)
{
Call(); // don't wait for result here
int expectedI = elapsed.Elapsed.TotalSeconds * numberOfCallsPerSecond;
if (i > expectedI) await Task.Delay(delay);
}
}

关于c# - 在 for 中使用 await Task.Delay 会降低性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26716006/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com