2012-07-05 11 views
10

Mam wymagania w moim projekcie (C#, VS2010, .NET 4.0), że konkretna pętla for musi zakończyć się w ciągu 200 milisekund. Jeśli tak się nie stanie, musi zakończyć się po tym czasie bez wykonywania pozostałych iteracji. Pętla ogólnie idzie od i = 0 do około 500 000 do 700 000, więc całkowity czas pętli zmienia się.Zakończ pętlę po określonym czasie w C#

Znam następujące pytania, które są podobne, ale nie pomogło w moim przypadku:

  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

Do tej pory próbowałem za pomocą Stopwatch obiekt do śledzenia czas, który minął, ale nie działa dla mnie. Tu są 2 różne sposoby próbowałem dotąd:

Metoda 1. Porównywanie upływ czasu w ciągu for pętli:

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

To nie działa, ponieważ if (sw.Elapsed > TimeSpan.FromMilliseconds(200)) trwa dłużej niż 200 milisekund, aby zakończyć. Stąd bezużyteczny w moim przypadku. Nie jestem pewien, czy to trwa tak długo, czy jest to w moim przypadku z jakiegoś powodu.

Metoda 2. Tworzenie oddzielnego wątku porównać czas:

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

mam jakiś inny kod w pętli for, która drukuje kilka statystyk. Mówi mi, że w przypadku metody 2, pętla for zdecydowanie zrywa się przed zakończeniem wszystkich iteracji, ale czas pętli jest nadal 280-300 milisekund.

Jakieś sugestie dotyczące przerwania pętli for z dokładnością do 200 milisekund lub mniej? Dzięki.

+0

Co powiesz na zabicie wątku, który uruchamia tę pętlę? Może nie jest jednak idealny. –

+0

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

+0

@Filip Ekberg, Dobry punkt! Trzeba to naprawić. :) Jednak może nie pomoże mi to naprawić mojego problemu. To dobra praktyka. Dzięki. – silverspoon

Odpowiedz

5

Dla szybszego porównania spróbować porównując

if(sw.ElapsedMilliseconds > 200) 
    break; 

powinien to zrobić czek na początku działalności swojej pętli, a także w trakcie przetwarzania („// Czy niektóre rzeczy” część kodu), ponieważ jest możliwe, na przykład, że przetwarzanie rozpoczyna się od 190 (początek pętli), trwa 20 i kończy na 210.

Można również zmierzyć średni czas realizacji przetwarzania (jest to przybliżone, ponieważ opiera się na średnim czasie), w ten sposób pętla powinna trwać 200 milisekund lub mniej, tutaj jest wersja demonstracyjna, którą można umieścić w głównej metodzie aplikacja konsoli i łatwo zmodyfikować go do swojej aplikacji:

 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

Sprawdzanie czasu w wielu miejscach jest dobrym pomysłem. Mój kod zachowuje się znacznie lepiej dzięki "if (sw.ElapsedMilliseconds> 200)". Dzięki. I tak, już mierzę średni czas na pętlę. – silverspoon

+0

Właśnie skończyłem testowanie mojej aplikacji po dodaniu bitu "sw.ElapsedMilliseconds". Chociaż teraz jestem w stanie przerwać pętlę w określonym czasie, wyniki są bardzo podobne do moich poprzednich obserwacji - sw.ElapsedMilliseconds zajmuje dużo czasu na obliczenie. Stąd całkowity czas pętli wynosi około 3 sekund (zamiast 280-300 ms), jeśli pozwolę, aby pętla była kontynuowana bez zerwania po użyciu "sw.ElapsedMilliseconds". Bardzo dziwny. – silverspoon

+0

Spróbuj przetestować pętlę bez instrukcji break z 2 przypadkami: w pierwszym przypadku zamiast w przypadku '(sw.ElapsedMilliseconds + avg> 200) break;' użyj '(sw.ElapsedMilliseconds + avg> 200) {}' i w drugim przypadku komentarza out EllipsedMilliseconds comparsion, tak jak to '//(sw.ElapsedMilliseconds + avg> 200) {}', różnica czasu między dwoma przypadkami jest czasem narzuconym przez ElapsedMilliseconds comparsion (s). –

1

Skorzystaj z pierwszego - prostego i masz większe szanse być dokładniejszym niż drugi.

Oba przypadki mają taki sam warunek zakończenia, więc oba powinny się zachowywać mniej więcej tak samo. Drugi jest znacznie bardziej skomplikowany ze względu na użycie wątków i uśpienia, więc użyłbym pierwszego. Również druga z nich jest znacznie mniej precyzyjna ze względu na sen.

Nie ma absolutnie żadnego powodu, dla którego TimeSpan.FromMilliseconds(200) może zająć znaczącą ilość czasu (jak również wywoływać go w każdej iteracji).

+0

Również w drugiej metodzie będziesz mieć warunki wyścigu podczas uzyskiwania dostępu do 'bDoExit'. Zamiast tego można zreorganizować drugą metodę i użyć tokenu anulowania (http://msdn.microsoft.com/en-us/library/dd997364.aspx). Zostały one zaprojektowane dla _asking_ operacji do zatrzymania. –

+0

@Jasd, nie nazwałbym tego wyścigu, ponieważ jest tylko jeden pisarz. Kod oczywiście ma szansę nigdy nie działać bez 'volatile' (ponieważ może być zoptymalizowany w pętli ...), ale jeśli to w końcu doprowadzi do zmiany w zmiennej boolean (ponieważ powoduje tylko pojedyncze przejścia false-> prawdziwe). –

+0

Masz rację, nie może być warunku wyścigu, ponieważ czytanie i pisanie boolean jest operacją atomową. Mój błąd. –

0

nie wiem, czy to jest to, że dokładnie, ale myślę, że warto spróbować użyć 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

Innym rozwiązaniem byłoby używać CancellationTokenSo urce:

CancellationTokenSource source = new CancellationTokenSource(100); 

while(!source.IsCancellationRequested) 
{ 
    // Do stuff 
} 
Powiązane problemy