2012-07-05 18 views
10

Tengo un requisito en mi proyecto (C#, VS2010, .NET 4.0) que un bucle particular for debe terminar dentro de los 200 milisegundos. Si no lo hace, entonces tiene que terminar después de esta duración sin ejecutar las iteraciones restantes. El bucle generalmente va para i = 0 a alrededor de 500,000 a 700,000, por lo que el tiempo total de bucle varía.Salir del ciclo después de una hora específica en C#

He leído las preguntas siguientes que son similares pero no ayudó en mi caso:

  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

Hasta aquí he intentado usar un objeto para realizar un seguimiento Stopwatch el tiempo transcurrido, pero no funciona para mí. Aquí hay 2 métodos diferentes que he probado hasta ahora:

Método 1. comparar el tiempo transcurrido dentro for bucle:

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(); 

Esto no funciona porque if (sw.Elapsed > TimeSpan.FromMilliseconds(200)) lleva más de 200 milisegundos en completarse. De ahí que sea inútil en mi caso. No estoy seguro de si TimeSpan.FromMilliseconds() generalmente toma tanto tiempo o simplemente está en mi caso por alguna razón.

método 2. Creación de un hilo separado para comparar el tiempo:

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(); 

tengo algún otro código en el bucle for que imprime algunas estadísticas. Me dice que en el caso del Método 2, el ciclo for definitivamente se rompe antes de completar todas las iteraciones, pero el tiempo del ciclo sigue siendo 280-300 milisegundos.

¿Alguna sugerencia para romper un bucle estrictamente dentro de 200 milisegundos o menos? Gracias.

+0

¿Qué tal matar el hilo que ejecuta ese bucle? Sin embargo, podría no ser ideal. –

+0

http://stackoverflow.com/questions/5945533/how-to-execute-the-loop-for-specific-time – akhil

+0

@Filip Ekberg, buen punto! Necesito arreglarlo :) Sin embargo, puede que no me ayude a solucionar mi problema. Sin embargo, es una buena práctica. Gracias. – silverspoon

Respuesta

5

Para una comparación más rápido intente comparar

if(sw.ElapsedMilliseconds > 200) 
    break; 

Usted debe hacer el registro de entrada en el beggining de su bucle y también durante el procesamiento, ("// hacer algunas cosas" parte del código), ya que es Es posible, por ejemplo, que el procesamiento comience en 190 (comienzo del ciclo), que dure 20 y termine en 210.

También puede medir el tiempo promedio de ejecución de su procesamiento (esto es aproximado porque depende del tiempo promedio), de esta manera el ciclo debe durar 200 milisegundos o menos, aquí hay una demostración que puede poner en un método Principal de aplicación de consola y fácil modificar para su aplicación:

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

Verificar la sincronización en múltiples lugares es una buena idea. Mi código se comporta mucho mejor ahora con "if (sw.ElapsedMilliseconds> 200)". Gracias. Y sí, ya mido el tiempo promedio por ciclo. – silverspoon

+0

Acabo de terminar de probar mi aplicación después de agregar el bit 'sw.ElapsedMilliseconds'. Aunque ahora puedo romper el ciclo dentro del tiempo especificado, los resultados son muy similares a mis observaciones anteriores: sw.ElapsedMilliseconds tarda mucho tiempo en calcular. Por lo tanto, el tiempo total de bucle es de aproximadamente 3 segundos (en lugar de 280-300ms) si dejo que el bucle continúe sin romperse después de usar 'sw.ElapsedMilliseconds'. Muy extraño. – silverspoon

+0

Pruebe el bucle sin declaración de interrupción con 2 casos: en el primer caso en lugar de si '(sw.ElapsedMilliseconds + avg> 200) se rompe;' use '(sw.ElapsedMilliseconds + avg> 200) {}' y en el segundo caso comente la comparación de ElapsedMilliseconds como esta '//(sw.ElapsedMilliseconds + avg> 200) {}', la diferencia de tiempo entre dos casos es la sobrecarga de tiempo agregada por la (s) comparsión (s) de ElapsedMilliseconds. –

1

Use la primera: simple y tenga mejores posibilidades de ser más preciso que la segunda.

Ambos casos tienen el mismo tipo de condición de terminación, por lo que ambos deben comportarse son más o menos lo mismo. En segundo lugar es mucho más complicado debido al uso de hilos y Sleep, así que usaría el primero. También el segundo es mucho menos preciso debido a que duerme.

Absolutamente no hay razones para TimeSpan.FromMilliseconds(200) para tomar una cantidad significativa de tiempo (así como para llamarlo en cada iteración).

+0

También en el segundo método tendrá condiciones de carrera al acceder 'bDoExit'. En su lugar, podría refactorizar el segundo método y usar algún tipo de token de cancelación (http://msdn.microsoft.com/en-us/library/dd997364.aspx). Están diseñados para realizar una operación de detención. –

+0

@Jasd, no lo llamaría condición de raza ya que solo hay un escritor. Obviamente, el código tiene la posibilidad de no funcionar nunca sin 'volátil '(ya que podría optimizarse en bucle ...), pero si lo hiciera, eventualmente captaría el cambio en la variable booleana (ya que solo hace transiciones únicas falsas-> cierto). –

+0

Tiene razón, no puede haber una condición de carrera porque leer y escribir un booleano es una operación atómica. Mi error. –

0

no sé si esto es eso exactamente, pero creo que vale la pena intentar usar un 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; 
    } 
} 
1

Otra opción sería utilizar CancellationTokenSo urce:

CancellationTokenSource source = new CancellationTokenSource(100); 

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