C# 精确替代线程。睡眠

C# 精确替代线程。睡眠,c#,.net,C#,.net,我有一个方法Limit(),它计算某个通道在特定时间内通过的带宽,并使用Thread.Sleep()限制带宽(如果达到带宽限制)。 方法本身会产生适当的(在我看来是结果)但Thread.Sleep不会(由于多线程CPU的使用),因为我有适当的“毫秒Towait”,但之后的速度检查远远不是我通过的限制 有没有办法使限制更精确? 限制器类 private readonly int m_maxSpeedInKbps; public Limiter(int maxSpeedInKbps)

我有一个方法Limit(),它计算某个通道在特定时间内通过的带宽,并使用Thread.Sleep()限制带宽(如果达到带宽限制)。 方法本身会产生适当的(在我看来是结果)但Thread.Sleep不会(由于多线程CPU的使用),因为我有适当的“毫秒Towait”,但之后的速度检查远远不是我通过的限制

有没有办法使限制更精确?

限制器类

    private readonly int m_maxSpeedInKbps;
    public Limiter(int maxSpeedInKbps)
    {
        m_maxSpeedInKbps = maxSpeedInKbps;
    }

    public int Limit(DateTime startOfCycleDateTime, long writtenInBytes)
    {
        if (m_maxSpeedInKbps > 0)
        {
            double totalMilliseconds = DateTime.Now.Subtract(startOfCycleDateTime).TotalMilliseconds;
            int currentSpeedInKbps = (int)((writtenInBytes / totalMilliseconds));
            if (currentSpeedInKbps - m_maxSpeedInKbps > 0)
            {
                double delta = (double)currentSpeedInKbps / m_maxSpeedInKbps;
                int millisecondsToWait = (int)((totalMilliseconds * delta) - totalMilliseconds);
                if (millisecondsToWait > 0)
                {
                    Thread.Sleep(millisecondsToWait);
                    return millisecondsToWait;
                }
            }
        }

        return 0;
    }
在大增量中总是失败的测试类

[TestMethod]
public void ATest()
{
    List<File> files = new List<File>();
    for (int i = 0; i < 1; i++)
    {
        files.Add(new File(i + 1, 100));
    }

    const int maxSpeedInKbps = 1024; // 1MBps
    Limiter limiter = new Limiter(maxSpeedInKbps);

    DateTime startDateTime = DateTime.Now;
    Parallel.ForEach(files, new ParallelOptions {MaxDegreeOfParallelism = 5}, file =>
    {
        DateTime currentFileStartTime = DateTime.Now;
        Thread.Sleep(5);
        limiter.Limit(currentFileStartTime, file.Blocks * Block.Size);
    });

    long roundOfWriteInKB = (files.Sum(i => i.Blocks.Count) * Block.Size) / 1024;
    int currentSpeedInKbps = (int) (roundOfWriteInKB/DateTime.Now.Subtract(startDateTime).TotalMilliseconds*1000);

    Assert.AreEqual(maxSpeedInKbps, currentSpeedInKbps, string.Format("maxSpeedInKbps {0} currentSpeedInKbps {1}", maxSpeedInKbps, currentSpeedInKbps));
} 
[TestMethod]
公共无效测试()
{
列表文件=新列表();
对于(int i=0;i<1;i++)
{
添加(新文件(i+1100));
}
const int maxSpeedInKbps=1024;//1MBps
限制器限制器=新限制器(maxSpeedInKbps);
DateTime startDateTime=DateTime.Now;
ForEach(文件,新的并行选项{MaxDegreeOfParallelism=5},文件=>
{
DateTime currentFileStartTime=DateTime.Now;
睡眠(5);
limiter.Limit(currentFileStartTime,file.Blocks*Block.Size);
});
long-roundOfWriteInKB=(files.Sum(i=>i.Blocks.Count)*Block.Size)/1024;
int currentSpeedInKbps=(int)(roundOfWriteInKB/DateTime.Now.Subtract(startDateTime.TotalMillimes*1000);
AreEqual(maxSpeedInKbps,currentSpeedInKbps,string.Format(“maxSpeedInKbps{0}currentSpeedInKbps{1}”,maxSpeedInKbps,currentSpeedInKbps));
} 

我过去经常使用
线程。在我发现之前,我经常睡觉。使用waithandles可以挂起线程,当从别处触发waithandle或达到时间阈值时,线程将再次激活。也许可以重新设计您的限制方法,以某种方式使用waithandles,因为在许多情况下,它们确实比Thread.Sleep更精确。

您可以使用繁忙的等待来相当准确地完成此任务,但我不推荐这样做。你应该使用等待来代替

但是,此方法将相当准确地等待:

void accurateWait(int millisecs)
{
    var sw = Stopwatch.StartNew();

    if (millisecs >= 100)
        Thread.Sleep(millisecs - 50);

    while (sw.ElapsedMilliseconds < millisecs)
        ;
}
void accurateWait(整数毫秒)
{
var sw=Stopwatch.StartNew();
如果(毫秒>=100)
睡眠(毫秒-50);
而(sw.ElapsedMillistes<毫秒)
;
}
但这是一个繁忙的等待,会严重消耗CPU周期。它还可能受到垃圾收集或任务重新调度的影响

以下是测试程序:

using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Threading;

namespace Demo
{
    class Program
    {
        void run()
        {
            for (int i = 1; i < 10; ++i)
                test(i);

            for (int i = 10; i < 100; i += 5)
                test(i);

            for (int i = 100; i < 200; i += 10)
                test(i);

            for (int i = 200; i < 500; i += 20)
                test(i);
        }

        void test(int millisecs)
        {
            var sw = Stopwatch.StartNew();
            accurateWait(millisecs);
            Console.WriteLine("Requested wait = " + millisecs + ", actual wait = " + sw.ElapsedMilliseconds);
        }

        void accurateWait(int millisecs)
        {
            var sw = Stopwatch.StartNew();

            if (millisecs >= 100)
                Thread.Sleep(millisecs - 50);

            while (sw.ElapsedMilliseconds < millisecs)
                ;
        }

        static void Main()
        {
            new Program().run();
        }
    }
}
使用系统;
使用系统诊断;
使用System.Collections.Generic;
使用系统线程;
名称空间演示
{
班级计划
{
无效运行()
{
对于(int i=1;i<10;++i)
试验(i);
对于(int i=10;i<100;i+=5)
试验(i);
对于(int i=100;i<200;i+=10)
试验(i);
对于(int i=200;i<500;i+=20)
试验(i);
}
无效测试(整数毫秒)
{
var sw=Stopwatch.StartNew();
准确等待(毫秒);
Console.WriteLine(“请求的等待=“+毫秒+”,实际等待=“+sw.ElapsedMilliseconds”);
}
无效准确等待(整数毫秒)
{
var sw=Stopwatch.StartNew();
如果(毫秒>=100)
睡眠(毫秒-50);
而(sw.ElapsedMillistes<毫秒)
;
}
静态void Main()
{
新程序().run();
}
}
}

你说的“远”到底是什么意思?你说的有多远?你确定
线程.Sleep
阻止更多数据进入频道吗?虽然这是真的,你不会得到很多的精度,我不相信这是你的问题。您多久调用一次
Limit
?如果您睡眠时间较短,那么实际睡眠时间将不准确-线程时间片为15ms,因此将其加倍或三倍,以获得合理精度的最小增量。但是,你可以用秒表来测量你的实际睡眠时间:实际上在Windows 7和更高版本中,
Thread.Sleep()
非常精确-但是
DateTime。现在
肯定不是。我明白了,这个问题没有明确说明你发送的是什么。我盲目地假设您正在接收,似乎忽略了参数名为
writeninbytes
的事实。无论如何,如果延迟超过300毫秒,我觉得
Thread.Sleep
应该足够准确,因为您多次调用
Limit
。与块的大小(我相信你说过50KB)相比,总数据大小是多少?您是否愿意发布一个小的可编译示例来说明问题?另外,我支持@MatthewWatson,因为你应该使用
秒表而不是
日期时间。现在
。你的意思是限制使用WaitHandle,还是用它来包装真正的带宽发送/读取任务,而不是并行。如果现在包装每个任务?当我回答这个问题时,这只是对标题的快速反应“Thread.Sleep的精确替代品”,因为毫无疑问,在某些情况下,waithandles可能比Thread.Sleep更精确。但是,我不熟悉您的带宽限制问题,因此我不确定它们是否适用于这种情况。问题可能是平行的。ForEach()我在其中运行每个IO线程。如果我将并行度限制为1,则结果是精确的。必须是thread.sleep,按您所说停止所有线程,而不是它运行的线程。@eugeneK thread.sleep()真的没有停止所有线程。那太可怕了!