2009-06-17 10 views
18

Ist es normales Verhalten, dass Stoppuhr negative Werte zurückgeben kann? Das folgende Codebeispiel kann verwendet werden, um es zu reproduzieren.System.Diagnostics.Stopwatch gibt negative Werte in Elapsed ... Eigenschaften zurück

while (true) 
     { 
      Stopwatch sw = new Stopwatch(); 
      sw.Start(); 
      sw.Stop(); 

      if (sw.ElapsedMilliseconds < 0) 
       Debugger.Break(); 

     } 

Der einzige Ort, wo ich negative Zahlen wiedergeben kann, ist meine virtuelle Maschine (gehostet von Hyper-V auf einer 8-Core-Maschine)

+0

Was ist der Wert von ElapsedMilliseconds ist, wenn negativ? –

+18

Dein Flux-Kondensator deinstallieren. –

+0

Es ändert sich jedes Mal. Es kann mehrere Schleifenspins geben, wenn eine negative Zahl auftritt. –

Antwort

17

Dies ist ein bug. Es scheint nicht viel Aufmerksamkeit zu haben, also würde ich vorschlagen, diesen Bericht weiter zu verfolgen.

Die uninspiring workaround erscheint negative Werte zu ignorieren zu sein:

long elapsedMilliseconds = Math.Max(0, stopwatch.ElapsedMilliseconds); 
+0

Es scheint, dass die einzige Möglichkeit, es zu reproduzieren, eine virtuelle Maschine ist. Ich habe den Code auf mehreren Desktop-Rechnern ausprobiert - kein Erfolg –

+4

Dieses Workaround eliminiert nur negative Werte - es beseitigt nicht die extrem hohen (falschen) positiven Werte, die es auch zurückgibt. –

+0

Es funktioniert in .NET 4, aber ist schwer zu glauben, dass der Fehler immer noch in .NET 3.5 :( – vtortola

0

Die "Geschlossen als Fixed" Resolution über die Microsoft Connect Bug muss bedeuten, dass sie es in .NET festgelegt 4. Ich habe die Repro-Code auf ausführen mein Desktop und eine VM für mehrere Minuten und habe keine negativen Werte.

+3

Es ist nicht vollständig in .NET 4 behoben, siehe meine Kommentare im Connect-Bericht und in meiner Antwort ... –

10

Ich dekompilierte die Stoppuhr-Klasse in .NET 2.0 und .NET 4.0 mit Reflector und dann verglichen die Unterschiede, um zu sehen, wie es behoben wurde. Neben dem Hinzufügen der neuen Restart Methode, dies ist alles, was ich für einen Unterschied gefunden:

public void Stop() 
{ 
    if (this.isRunning) 
    { 
     long num2 = GetTimestamp() - this.startTimeStamp; 
     this.elapsed += num2; 
     this.isRunning = false; 
// THE NEXT 4 LINES ARE NEW IN .NET 4.0: 
     if (this.elapsed < 0L) 
     { 
      this.elapsed = 0L; 
     } 
    } 
} 

Also im Grunde setzten sie auf 0 in der Stop-Methode abgelaufen, wenn der Wert negativ ist. Es gibt noch Bugs IMHO:

  1. Was passiert, wenn der Benutzer eine der Eigenschaften Elapsed vor dem Stopp der Stoppuhr liest? Sie können immer noch einen negativen Wert erhalten.
  2. Zurücksetzen auf 0 ist nicht korrekt. Einige Zeit musste verstrichen sein, auch wenn es nur ein paar Mikrosekunden war!
  3. Es tut nichts, um die ungewöhnlich großen positiven abgelaufenen Werte zu behandeln, die ich und andere gemeldet haben.

EDIT: Leider # 2 und # 3 sind über die Leistung von .NET Framework:

Hier ist der Kern des Problems: von MSDN auf QueryPerformanceCounter, welche die API durch die Stoppuhr verwendet wird, Klasse:

Auf einem Multiprozessor-Computer sollte es keinen Unterschied machen, welcher Prozessor aufgerufen wird. Sie können jedoch unterschiedliche Ergebnisse auf verschiedenen Prozessoren aufgrund von Fehlern im BIOS (Basic Input/Output System) oder Hardware-Abstraktionsschicht (HAL) erhalten. Verwenden Sie die SetThreadAffinityMask-Funktion, um Prozessoraffinität für einen -Thread anzugeben.

Verwenden Sie nicht nur .NET 4.0 Stoppuhr und gehen Sie davon aus, dass das Problem behoben ist. Es ist nicht und sie können nichts dagegen tun, es sei denn du willst, dass sie mit deiner Thread-Affinität meckern. Aus der Stoppuhr-Klassendokumentation:

Auf einem Multiprozessor-Computer spielt es keine Rolle, auf welchem ​​Prozessor der -Thread ausgeführt wird. Aufgrund von Fehlern im BIOS oder der Hardware Abstraction Layer (HAL) können Sie jedoch unterschiedliche Timing-Ergebnisse für verschiedene Prozessoren erhalten.Um die Prozessoraffinität für einen Thread anzugeben, verwenden Sie die ProcessThread.ProcessorAffinity-Methode.

-1

Die einzige Lösung, die ich gefunden habe, ist die richtige verstrichene Zeit in einer VM zu erhalten (VB):

Dim tstart AS DateTime = Now 
...executing code... 
Dim telapsed = (Now - tstart).TotalMilliseconds 
+2

Warum die down vote? –

+0

downvote war ich nicht ... aber vielleicht wegen der 'Ticks'-Eigenschaft von' DateTime.Now 'hat nicht die gleiche Auflösung wie eine' Stoppuhr' (soweit ich mich erinnere, werden die 'Ticks' von' DateTime.Now' nur alle 15ms aktualisiert) –

+0

Beachten Sie, dass die 'Now' Eigenschaft bei Tageslicht dramatisch vor- oder zurückspringt Es ist auch viel, viel langsamer als "UtcNow". – James

Verwandte Themen