2012-07-05 8 views
10

Nel mio progetto (C#, VS2010, .NET 4.0) ho un requisito che un particolare ciclo for deve terminare entro 200 millisecondi. In caso contrario, deve terminare dopo questa durata senza eseguire le iterazioni rimanenti. Il ciclo generalmente va da i = 0 a circa da 500.000 a 700.000, quindi il tempo di ciclo totale varia.Esci dal ciclo dopo un periodo di tempo specifico in C#

Ho letto le domande seguenti che sono simili, ma non ha aiutato nel mio 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

Finora ho provato ad utilizzare un oggetto Stopwatch per tenere traccia il tempo trascorso ma non funziona per me. Ecco 2 metodi differenti ho provato finora:

Metodo 1. Confrontando il tempo trascorso all'interno for ciclo:

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

Ciò non funziona perché if (sw.Elapsed > TimeSpan.FromMilliseconds(200)) impiega 200 millisecondi per completare. Quindi inutile nel mio caso. Non sono sicuro che lo standard TimeSpan.FromMilliseconds() richieda questo tempo o è solo nel mio caso per qualche motivo.

Metodo 2. Creazione di un thread separato per confrontare il tempo:

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

ho qualche altro codice nel ciclo for che stampa alcune statistiche. Mi dice che in caso di Metodo 2, il ciclo for si interrompe definitivamente prima di completare tutte le iterazioni ma il tempo di ciclo è ancora 280-300 millisecondi.

Qualche suggerimento per interrompere un ciclo for strettamente con-in 200 millisecondi o meno? Grazie.

+0

Come su uccidere il thread che esegue quel ciclo? Potrebbe non essere l'ideale però. –

+0

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

+0

@Filip Ekberg, buon punto! Hai bisogno di aggiustarlo. :) Tuttavia potrebbe non aiutarmi a risolvere il mio problema. È comunque una buona pratica. Grazie. – silverspoon

risposta

5

Per un confronto più veloce provare a confronto

if(sw.ElapsedMilliseconds > 200) 
    break; 

Si dovrebbe fare il check-in che beggining del vostro ciclo e anche durante la lavorazione, ("// fare alcune cose" parte del codice), perché è possibile, ad esempio, che l'elaborazione inizi a 190 (inizio del ciclo), dura 20 e termina a 210.

È anche possibile misurare il tempo di esecuzione medio dell'elaborazione (questo è approssimativo perché si basa su un tempo medio), in questo modo il loop dovrebbe durare 200 millisecondi o meno, ecco una demo che è possibile inserire in un metodo Main di applicazione Console e facilmente modificarlo per la vostra applicazione:

 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

Controllare la tempistica in più punti è una buona idea. Il mio codice si comporta molto meglio ora con "if (sw.ElapsedMilliseconds> 200)". Grazie. E sì, ho già misurato il tempo medio per ciclo. – silverspoon

+0

Ho appena finito di testare la mia applicazione dopo aver aggiunto il bit "sw.ElapsedMilliseconds". Anche se ora sono in grado di interrompere il ciclo entro il tempo specificato, i risultati sono molto simili alle mie osservazioni precedenti: sw.ElapsedMilliseconds richiede molto tempo per il calcolo. Quindi il tempo di ciclo totale è di circa 3 secondi (anziché 280-300 ms) se lasciamo che il ciclo continui senza interruzioni dopo aver usato "sw.ElapsedMilliseconds". Molto strano. – silverspoon

+0

Provare a testare l'istruzione loop with break con 2 casi: nel primo caso invece di if '(sw.ElapsedMilliseconds + avg> 200) break;' use '(sw.ElapsedMilliseconds + avg> 200) {}' e nel secondo caso comment con il comparsio ElapsedMilliseconds come questo '//(sw.ElapsedMilliseconds + avg> 200) {}', la differenza di tempo tra due casi è il sovraccarico di tempo che viene aggiunto dalle comparsioni di ElapsedMilliseconds. –

1

Utilizzare il primo - semplice e avere migliori possibilità di precisione rispetto al secondo.

Entrambi i casi hanno lo stesso tipo di condizione di terminazione, quindi entrambi dovrebbero comportarsi più o meno lo stesso. Il secondo è molto più complicato a causa dell'uso di thread e Sleep, quindi userei il primo. Anche il secondo è molto meno preciso a causa del sonno.

Non ci sono assolutamente motivi per il TimeSpan.FromMilliseconds(200) di prendere una quantità significativa di tempo (oltre a chiamarlo in ogni iterazione).

+0

Anche nel secondo metodo si avranno condizioni di gara quando si accede a 'bDoExit'. Invece è possibile rifattorizzare il secondo metodo e utilizzare una sorta di token di cancellazione (http://msdn.microsoft.com/en-us/library/dd997364.aspx). Sono progettati per interrompere l'operazione. –

+0

@Jasd, non lo chiamerei race condition in quanto esiste un solo writer. Ovviamente il codice ha la possibilità di non funzionare mai senza 'volatile' (dato che potrebbe essere ottimizzato in loop ...), ma se lo farà alla fine capterà cambiamenti nella variabile booleana (dato che rende false solo le singole transizioni-> vero). –

+0

Hai ragione, non ci può essere una condizione di competizione perché leggere e scrivere un booleano è un'operazione atomica. Colpa mia. –

0

non so se questo è che esattamente, ma penso che la pena di provare a utilizzare 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

Un'altra opzione sarebbe usare CancellationTokenSo urza:

CancellationTokenSource source = new CancellationTokenSource(100); 

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