假设我想开始大致每秒分配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 seconds
。
似乎等待Task.Delay增加了很多开销(当然没有它就可以在3ms内生成调用。
我没想到await会在这种情况下增加很多开销。这是正常的吗?
编辑:
请忘记电话()。运行此代码显示类似的结果:
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倍
答案 0 :(得分:14)
Task.Delay
重量轻但不准确。由于没有延迟的循环完成得更快,听起来你的线程正在空闲并使用操作系统休眠等待计时器过去。根据OS线程调度量程(在执行线程抢占的同一中断处理程序中)检查定时器,默认为16ms。
你可以用timeBeginPeriod
减少量子,但是如果你需要速率限制而不是精确的时间,那么更好(更节能)的方法是跟踪经过的时间(Stopwatch
类是好的为了这个)和拨打的电话数量,只有在拨打电话时才能延迟。总体效果是你的线程每秒会被唤醒~60次,并且每次都会启动一些工作项。如果您的CPU忙于其他事情,当您获得控制权时,您将启动额外的工作项目 - 尽管如果这就是您需要的,那么同时启动项目的数量也非常简单。
public async Task Generate(int numberOfCallsPerSecond)
{
var elapsed = Stopwatch.StartNew();
var delay = TimeSpan.FromMiliseconds(1000/numberOfCallsPerSecond); // a call should happen every 1000 / numberOfCallsPerSecond miliseconds
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);
}
}
答案 1 :(得分:1)
我的通灵调试器说你的Call
方法有一个重要的同步部分(即await
之前的部分)需要时间同步执行。
如果您希望Generate
方法仅“激活”这些Call
调用并让它们同时运行(包括同步部分),则需要将它们卸载到ThreadPool
个线程使用Task.Run
:
var task = Task.Run(() => Call());
await Task.Delay(delay);
Task.Delay
几乎没有任何开销。它在内部使用System.Threading.Timer
,只需要很少的资源。
答案 2 :(得分:-1)
如果你使用Task.Delay()的时间跨度,它将杀死CPU。使用整数,它不会。真实的故事。不明白为什么。