13

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

我已经阅读了以下类似的问题,但它们对我的情况没有帮助:

  1. 在 C++ 中经过 30 毫秒的时间后退出循环的最佳方法是什么
  2. 如何在特定时间执行循环

到目前为止,我已经尝试使用一个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 循环?谢谢。

4

5 回答 5

9

为了更快的比较尝试比较

if(sw.ElapsedMilliseconds > 200)
   break;

您应该在循环开始时以及在处理过程中进行检查(“// Do some stuff”部分代码),因为例如,处理可能从 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();
于 2012-07-05T07:11:46.523 回答
2

另一种选择是使用 CancellationTokenSource:

CancellationTokenSource source = new CancellationTokenSource(100);

while(!source.IsCancellationRequested)
{
    // Do stuff
}
于 2017-01-10T23:01:33.803 回答
1

使用第一个 - 简单并且比第二个更准确。

两种情况都具有相同的终止条件,因此两者的行为应该或多或少相同。由于线程和睡眠的使用,第二个要复杂得多,所以我会使用第一个。由于睡眠,第二个也不太精确。

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

于 2012-07-05T07:08:18.643 回答
1

使用取消令牌:

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

 while (!cancellationToken.IsCancellationRequested)
 {
     //Do stuff...
 }
于 2018-08-23T17:33:28.240 回答
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;
    }
}
于 2012-07-05T07:48:46.657 回答