在C#中的特定时间后退出循环

时间:2012-07-05 06:58:04

标签: c# benchmarking

我的项目(C#,VS2010,.NET 4.0)要求特定的for循环必须在200毫秒内完成。如果没有那么它必须在此持续时间之后终止而不执行剩余的迭代。循环通常为i = 0至约500,000至700,000,因此总循环时间变化。

我读过以下类似的问题,但在我的案例中没有帮助:

  1. What is the best way to exit out of a loop after an elapsed time of 30ms in C++
  2. How to execute the loop for specific time
  3. 到目前为止,我已尝试使用Stopwatch对象来跟踪已用时间,但它对我不起作用。到目前为止,我尝试了两种不同的方法:

    方法1。比较for循环中的已用时间:

    Stopwatch sw = new Stopwatch();
    sw.Start();
    
    for (i = 0; i < nEntries; i++) // nEntries is typically more than 500,000
    {
          // Do some stuff
          ...
          ...
          ...
    
          if (sw.Elapsed > TimeSpan.FromMilliseconds(200))
              break;
    }
    
    sw.Stop();
    

    这不起作用,因为if (sw.Elapsed > TimeSpan.FromMilliseconds(200))需要超过200毫秒才能完成。因此在我的情况下毫无用处。我不确定TimeSpan.FromMilliseconds()是否通常需要这么长时间,或者仅仅因为某些原因而在我的情况下。

    方法2。创建一个单独的线程来比较时间:

    Stopwatch sw = new Stopwatch();
    sw.Start();                    
    bool bDoExit = false;
    int msLimit = 200;
    
    System.Threading.ThreadPool.QueueUserWorkItem((x) =>
    {
         while (bDoExit == false)
         {
            if (sw.Elapsed.Milliseconds > msLimit)
            {
                bDoExit = true;
                sw.Stop();
             }
    
             System.Threading.Thread.Sleep(10);
          }
    
    });
    
    for (i = 0; i < nEntries; i++) // nEntries is typically more than 500,000
    {
          // Do some stuff
          ...
          ...
          ...
    
          if (bDoExit == true)
              break;
    }
    
    sw.Stop();
    

    我在for循环中有一些其他代码可以打印一些统计信息。它告诉我,在方法2的情况下,for循环在完成所有迭代之前肯定会中断,但循环时间仍然是280-300毫秒。

    在200毫秒或更短时间内严格打破for循环的任何建议? 感谢。

5 个答案:

答案 0 :(得分:6)

为了更快的比较,请尝试比较

if(sw.ElapsedMilliseconds > 200)
   break;

你应该检查循环的开始以及处理过程中(“//做一些东西”代码的一部分),因为例如,处理可以从190开始(开始时)循环),持续20,结束于210。

您还可以测量处理的平均执行时间(这是近似的,因为它依赖于平均时间),这种方式循环应该持续200毫秒或更短,这是一个可以放在控制台的Main方法中的演示应用程序并根据您的应用轻松修改它:

        Stopwatch sw = new Stopwatch();
        sw.Start();

        string a = String.Empty;
        int i;
        decimal sum = 0, avg = 0, beginning = 0, end = 0;
        for (i = 0; i < 700000; i++) // nEntries is typically more than 500,000
        {
            beginning = sw.ElapsedMilliseconds;
            if (sw.ElapsedMilliseconds + avg > 200)
                break;

            // Some processing
            a += "x";
            int s = a.Length * 100;
            Thread.Sleep(19);
            /////////////

            end = sw.ElapsedMilliseconds;
            sum += end - beginning;
            avg = sum / (i + 1);

        }
        sw.Stop();

        Console.WriteLine(
          "avg:{0}, count:{1}, milliseconds elapsed:{2}", avg, i + 1,
          sw.ElapsedMilliseconds);
        Console.ReadKey();

答案 1 :(得分:1)

使用第一个 - 简单,比第二个更精确的机会。

两种情况都有相同的终止条件,因此两者的行为大致相同。第二个是由于线程和睡眠的使用而复杂得多,所以我先使用第一个。由于睡眠,第二个也不太准确。

TimeSpan.FromMilliseconds(200)绝对没有理由花费任何大量时间(以及在每次迭代中调用它)。

答案 2 :(得分:1)

另一种选择是使用CancellationTokenSource:

CancellationTokenSource source = new CancellationTokenSource(100);

while(!source.IsCancellationRequested)
{
    // Do stuff
}

答案 3 :(得分:0)

我不知道这是不是确实如此,但我认为使用System.Timers.Timer值得一试:

int msLimit = 200;
int nEntries = 500000;
bool cancel = false;

System.Timers.Timer t = new System.Timers.Timer();
t.Interval = msLimit;
t.Elapsed += (s, e) => cancel = true;
t.Start();

for (int i = 0; i < nEntries; i++)
{
    // do sth

    if (cancel) {
        break;
    }
}

答案 4 :(得分:0)

使用取消令牌:

 var cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(15)).Token;

 while (!cancellationToken.IsCancellationRequested)
 {
     //Do stuff...
 }