2009-06-17 11 views
18

Est-ce un comportement normal que Stopwatch peut renvoyer des valeurs négatives? L'exemple de code ci-dessous peut être utilisé pour le reproduire.System.Diagnostics.Stopwatch renvoie des nombres négatifs dans Elapsed ... properties

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

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

     } 

Le seul endroit où je peux reproduire les nombres négatifs est ma machine virtuelle (hébergé par Hyper-V sur une machine 8-core)

+0

Quelle est la valeur de ElapsedMilliseconds lorsqu'elle est négative? –

+18

Désinstallez votre condensateur de flux. –

+0

Ça change à chaque fois. Il peut prendre plusieurs tours de boucle pour que le nombre négatif se produise. –

Répondre

17

Ceci est un bug. Il ne semble pas y avoir beaucoup d'attention à ce sujet, alors je suggérerais de donner suite à ce rapport.

Le uninspiring workaround semble ignorer les valeurs négatives:

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

Il semble que la seule façon de le reproduire est d'utiliser une machine virtuelle. J'ai essayé le code sur plusieurs machines de bureau - pas de succès –

+4

Cette solution de contournement élimine uniquement les valeurs négatives - elle n'élimine pas les valeurs extrêmement élevées (fausses) positives qu'elle renvoie également. –

+0

Cela fonctionne dans .NET 4, mais il est difficile de croire que le bogue existe toujours dans .NET 3.5 :( – vtortola

0

Le « Fermé Déterminée » résolution sur la Microsoft Connect Bug doit dire qu'ils fixés dans .NET 4. J'ai couru le code repro sur mon ordinateur de bureau et une machine virtuelle pendant plusieurs minutes et n'a pas eu de valeurs négatives.

+3

C'est pas complètement fixé dans .NET 4, voir mes commentaires dans le rapport Connect et dans ma réponse ... –

10

J'ai décompilé la classe Stopwatch dans .NET 2.0 et .NET 4.0 en utilisant Reflector, puis j'ai comparé les différences pour voir comment elle était réparée. En plus de l'ajout de la nouvelle méthode de redémarrage, cela est tout ce que je trouve une différence:

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; 
     } 
    } 
} 

Donc, fondamentalement, elles fixeraient se sont écoulées à 0 dans la méthode d'arrêt si la valeur est négative. Il ya encore des bogues IMHO:

  1. Que faire si l'utilisateur lit l'une des propriétés Elapsed avant d'arrêter le chronomètre? Vous pourriez toujours avoir une valeur négative.
  2. La réinitialisation à 0 n'est pas correcte. Certains temps devait s'être écoulé, même si ce n'était que quelques microsecondes!
  3. Il ne fait rien pour gérer les valeurs écoulées positives inhabituellement grandes que moi et d'autres ont rapportées.

EDIT: Malheureusement, # 2 et # 3 sont au-delà de la puissance du .NET Framework:

est ici au cœur du problème: à partir de MSDN sur QueryPerformanceCounter qui est l'API utilisée par le Chronomètre Sur un ordinateur multiprocesseur, peu importe le processeur appelé . Cependant, vous pouvez obtenir des résultats différents sur différents processeurs en raison de bogues dans le système d'entrée/sortie de base (BIOS) ou la couche d'abstraction matérielle (HAL). Pour spécifier l'affinité du processeur pour un thread , utilisez la fonction SetThreadAffinityMask. N'utilisez pas simplement le chronomètre .NET 4.0 et supposez que le problème est résolu. Ce n'est pas le cas et ils ne peuvent rien y faire, à moins que vous ne vouliez qu'ils aient de l'affinité avec votre thread. A partir de la documentation de la classe Chronomètre:

Sur un ordinateur multiprocesseur, peu importe quel processeur le fil fonctionne sur. Toutefois, en raison de bogues dans le BIOS ou le matériel Abstraction Layer (HAL) , vous pouvez obtenir différents résultats de synchronisation sur différents processeurs.Pour spécifier l'affinité du processeur pour un thread, utilisez la méthode ProcessThread.ProcessorAffinity.

-1

La seule solution que j'ai trouvé pour obtenir le temps écoulé correct dans une machine virtuelle est (VB):

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

Pourquoi le vote en baisse? –

+0

downvote n'était pas moi ... mais peut-être parce que la propriété 'Ticks' de' DateTime.Now 'n'a pas la même résolution qu'un' Stopwatch' (pour autant que je me souvienne, les 'Ticks' de' DateTime.Now' ne sont mis à jour que toutes les 15ms) –

+0

Notez que la propriété 'Now' va sauter en avant ou en avant de façon dramatique pendant la journée C'est aussi beaucoup, beaucoup plus lent que 'UtcNow' – James

Questions connexes