2013-05-26 83 views
2

我有一個方法Limit(),它在一定的時間內通過思考某個通道的帶寬,並通過使用Thread.Sleep()(如果達到帶寬限制)來限制它。 方法本身產生正確的(在我看來結果),但Thread.Sleep沒有(由於多線程的CPU使用),因爲我有適當的「毫秒toWait」,但速度檢查後,遠不是我已經通過的限制。Thread.Sleep的精確替代方法

有沒有辦法讓限制更精確?

限制器類

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

究竟你「遠」是什麼意思?你說多遠? –

+0

你確定'Thread.Sleep'能阻止進一步的數據進入通道嗎?雖然這是事實,但你不會得到很多精確度,我不相信這是你的問題。你多久打一次'限制'? – jerry

+0

如果您正在睡眠時間較短,那麼實際的睡眠時間將不準確 - 線程時間片爲15毫秒,因此對於最小三角洲來說,其雙倍或三倍值可提供合理的準確度。但是,您可以使用秒錶來衡量您睡眠的實際時間:http://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.aspx –

回答

3

我用Thread.Sleep了很多,直到我發現waithandles。使用等待句柄可以掛起線程,當等待句柄從其他地方觸發時,或者達到時間閾值時,線程將再次活動。也許有可能重新設計限制方法來以某種方式使用等待句柄,因爲在很多情況下,它們確實比Thread.Sleep更精確?

+0

你的意思是限制使用WaitHandle,或者用它來包裝真正的帶寬發送/閱讀任務,而不是使用Parallel.ForEach,它現在被包裝了嗎? – eugeneK

+0

當我回答這個答案時,這只是對「Thread.Sleep的精確替代」標題的快速反應,因爲毫無疑問,在某些情況下,等待句柄比「Thread.Sleep」更精確。不過,我並不熟悉你的帶寬限制問題,所以我不確定它們是否適用於這種情況。 – Stochastically

1

你可以用忙碌的等待相當準確地做到這一點,但我不會推薦它。您應該使用one of the multimedia timers來代替。

然而,這種方法會相當準確地等待:

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(); 
     } 
    } 
} 
+0

問題可能是Parallel.ForEach(),其中我運行每個IO線程。如果我將並行度限制爲1,結果是準確的。必須是thread.sleep,按照你所說的停止所有線程,而不是它所運行的那個線程。 – eugeneK

+0

@eugeneK Thread.Sleep()真的不會停止所有線程。那太可怕了! –

+0

不知不覺中你解決了我的問題...太棒了! – PawanS