2

特定の時間にいくつかのチャネルを通過した帯域幅をカウントし、 Thread.Sleep() を使用して制限するメソッド Limit() があります(帯域幅制限に達した場合)。メソッド自体は適切に生成されますが (私の意見では結果)、Thread.Sleep は生成されません (マルチスレッドの CPU 使用率のため)。これは、適切な "millisecondsToWait" があるためです。

制限をより正確にする方法はありますか?

リミッタークラス

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

2 に答える 2

3

ビジー待機を使用してかなり正確に実行できますが、お勧めしません。代わりに、マルチメディア タイマーの 1 つを使用して待機する必要があります。

ただし、このメソッドはかなり正確に待機します。

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

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

    while (sw.ElapsedMilliseconds < millisecs)
        ;
}

しかし、それはビジーな待機であり、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();
        }
    }
}
于 2013-05-26T13:35:02.177 に答える
3

waithandlesThread.Sleepを発見するまで、私はよく使っていました。待機ハンドルを使用すると、待機ハンドルが他の場所からトリガーされたとき、または時間のしきい値に達したときにスレッドを一時停止できます。多くの状況で実際には?Thread.Sleep

于 2013-05-26T12:42:53.933 に答える