使用等待Task.Delay在为杀死性能 [英] using await Task.Delay in a for kills performance

查看:2006
本文介绍了使用等待Task.Delay在为杀死性能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

比方说,我要开始每秒均匀分布的大约N个任务。

Let's say I want to start roughly N tasks per second distributed equally.

所以,我想这样的:

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 需要16秒我12核CPU。
看来的await Task.Delay增添了不少的开销(当然不到位产生的话费发生在3ms的。

At first I expected this to run in 1 second but for numberOfCallsPerSecond = 100 it takes 16 seconds on my 12 core CPU. It seems the await Task.Delay adds a lot of overhead (of course without it in place generation of the calls happens in 3ms.

我没有?预计等待将在此情况下加那么多的开销这是正常的。

I didn't expect that await would add so much overhead in this scenario. Is this normal?

编辑:

请忘记关于()的调用运行这段代码显示similiar结果:

Please forget about the Call(). Running this code shows similiar result:

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时,我以为生成来大约需要1第二个,不是10倍以上。

I tried to run it with numberOfCallsPerSecond = 500 and it takes around 10 seconds, I expected Generate to take roughly 1 second, not 10 times more

推荐答案

Task.Delay 是轻量级的,但不准确的。由于无延迟的循环完成得更快,这听起来像你的线程会闲置和使用OS的睡眠等待计时器的等待。计时器根据操作系统线程调度量子(在相同的中断处理它执行线程占先),这是16ms的默认选中

Task.Delay is lightweight but not accurate. Since the loop without delay completes much faster, it sounds like your thread is going idle and using an OS sleep to wait for the timer to elapse. The timer is checked according to the OS thread scheduling quantum (in the same interrupt handler which performs thread pre-emption), which is 16ms by default.

可以减少与 timeBeginPeriod 量子,但更好的(更节能)的方法,如果你需要的速率限制,而不是确切的时间是保持的经过时间的轨道(即秒表类是唯一的好本),并提出呼叫次数和延迟通话时已经赶上经过时间的。整体效果是,你的线程会醒来每秒〜60倍,并开始几个工作项各它的时间。如果你的CPU变成忙于别的事情,你会开始,当你得到控制权交还给额外的工作项目 - 尽管它也非常简单封顶的项目数量立刻开始,如果这就是你所需要的

You can reduce the quantum with timeBeginPeriod, but a better (more power efficient) approach if you need rate limiting rather than exact timing is to keep track of elapsed time (the Stopwatch class is good for this) and number of calls made, and only delay when calls made have caught up to elapsed time. The overall effect is that your thread will wake up ~60 times per second, and start a few work items each time it does. If your CPU becomes busy with something else, you'll start extra work items when you get control back -- although it's also pretty straightforward to cap the number of items started at once, if that's what you need.

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

这篇关于使用等待Task.Delay在为杀死性能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆