Question

Microsoft indique, dans la documentation GetTickCount, que vous ne pourriez jamais comparer tick compte pour vérifier si un intervalle est passé. par exemple:.

Mauvaise (pseudo-code):

DWORD endTime = GetTickCount + 10000; //10 s from now

...

if (GetTickCount > endTime)
   break;

Le code ci-dessus est mauvais parce qu'il est suceptable de renversement du compteur de la tique. Par exemple, supposons que l'horloge est à la fin de sa gamme:

endTime = 0xfffffe00 + 10000
        = 0x00002510; //9,488 decimal

Ensuite, vous effectuez votre chèque:

if (GetTickCount > endTime)

Ce qui est satisfait immédiatement, puisque GetTickCount est plus grand que endTime:

if (0xfffffe01 > 0x00002510)

La solution

vous devriez toujours lieu soustraire les deux intervalles de temps:

DWORD startTime = GetTickCount;

...

if (GetTickCount - startTime) > 10000 //if it's been 10 seconds
   break;

En regardant le même calcul:

if (GetTickCount - startTime) > 10000

if (0xfffffe01 - 0xfffffe00) > 10000

if (1 > 10000)

Ce qui est très bien en C / C ++, où le compilateur se comporte une certaine manière.

Mais qu'en est-Delphi?

Mais quand j'effectuer les mêmes mathématiques en Delphi, avec trop-plein de vérifier sur ({Q+}, {$OVERFLOWCHECKS ON}), la soustraction des deux chefs d'accusation de tiques génère une exception EIntOverflow lorsque la TickCount roule sur:

if (0x00000100 - 0xffffff00) > 10000

0x00000100 - 0xffffff00 = 0x00000200

Quelle est la solution destinée à ce problème?

Edit: j'ai essayé de désactiver temporairement OVERFLOWCHECKS:

{$OVERFLOWCHECKS OFF}]
   delta = GetTickCount - startTime;
{$OVERFLOWCHECKS ON}

Mais la soustraction jette encore une exception EIntOverflow.

Y at-il une meilleure solution, impliquant des moulages et des types de variables intermédiaires plus grandes?


Mise à jour

Une autre question SO j'ai demandé a expliqué pourquoi {$OVERFLOWCHECKS} ne fonctionne pas. Il fonctionne apparemment seulement à la Fonction niveau, pas la ligne niveau. Ainsi, alors que les ne pas travail:

{$OVERFLOWCHECKS OFF}]
   delta = GetTickCount - startTime;
{$OVERFLOWCHECKS ON}

les fait travail:

delta := Subtract(GetTickCount, startTime);

{$OVERFLOWCHECKS OFF}]
   function Subtract(const B, A: DWORD): DWORD;
   begin
      Result := (B - A);
   end;
{$OVERFLOWCHECKS ON}
Était-ce utile?

La solution

Que diriez-vous d'une fonction simple comme celui-ci?

function GetElapsedTime(LastTick : Cardinal) : Cardinal;
var CurrentTick : Cardinal;
begin
  CurrentTick := GetTickCount;
  if CurrentTick >= LastTick then
    Result := CurrentTick - LastTick
  else
    Result := (High(Cardinal) - LastTick) + CurrentTick;
end;

Vous avez donc

StartTime := GetTickCount
...
if GetElapsedTime(StartTime) > 10000 then
...

Il fonctionnera aussi longtemps que le temps entre StartTime et la GetTickCount actuelle est inférieure à la tristement célèbre 49,7 jours gamme de GetTickCount.

Autres conseils

Je l'ai cessé de faire ces calculs partout après avoir écrit quelques fonctions d'aide qui sont appelés à la place.

Pour utiliser le nouveau GetTickCount64() fonction sur Vista et plus tard, il est le nouveau type:

type
  TSystemTicks = type int64;

qui est utilisé pour tous ces calculs. GetTickCount() est jamais appelé directement, la fonction d'aide GetSystemTicks() est utilisée à la place:

type
  TGetTickCount64 = function: int64; stdcall;
var
  pGetTickCount64: TGetTickCount64;

procedure LoadGetTickCount64;
var
  DllHandle: HMODULE;
begin
  DllHandle := LoadLibrary('kernel32.dll');
  if DllHandle <> 0 then
    pGetTickCount64 := GetProcAddress(DllHandle, 'GetTickCount64');
end;

function GetSystemTicks: TSystemTicks;
begin
  if Assigned(pGetTickCount64) then
    Result := pGetTickCount64
  else
    Result := GetTickCount;
end;

// ...

initialization
  LoadGetTickCount64;
end.

Vous pouvez même suivre manuellement la pellicule autour de la valeur de retour de GetTickCount() et renvoyer un nombre tick vrai système 64 bits sur les systèmes antérieurs aussi, ce qui devrait fonctionner assez bien si vous appelez la fonction GetSystemTicks() au moins tous les quelques jours. [ Je crois me souvenir d'une mise en œuvre de ce quelque part, mais ne me souviens pas où il était. Gabr posté un lien et la de mise en œuvre.]

Maintenant, il est trivial de mettre en œuvre des fonctions telles que

function GetTicksRemaining(...): TSystemTicks;
function GetElapsedTicks(...): TSystemTicks;
function IsTimeRunning(...): boolean;

qui va masquer les détails. L'appel de ces fonctions au lieu de calculer des durées en place sert également de la documentation de l'intention de code, donc moins de commentaires sont nécessaires.

Modifier

Vous écrivez dans un commentaire:

  

Mais comme vous l'avez dit, les solutions de repli sur Windows 2000 et XP GetTickCount laisse encore le problème d'origine.

Vous pouvez corriger cela facilement. D'abord, vous n'êtes pas besoin pour revenir à GetTickCount() - vous pouvez utiliser le code fourni Gabr de calculer un 64 bits tick compter sur les anciens systèmes ainsi. (Vous pouvez remplacer timeGetTime() avec GetTickCount) si vous voulez.)

Mais si vous ne voulez pas faire que vous pouvez tout aussi bien désactiver les contrôles de portée et de débordement dans les fonctions d'aide, ou vérifier si le diminuende est plus petit que le soustractif et corriger cela en ajoutant 100.000.000 $ (2 ^ 32) pour simuler un nombre de graduation 64 bits. Ou mettre en œuvre les fonctions en assembleur, auquel cas le code n'a pas les chèques (pas que je conseillerais, mais il est possible).

Vous pouvez également utiliser DSiTimeGetTime64 de la DSiWin32 :

threadvar
  GLastTimeGetTime: DWORD;
  GTimeGetTimeBase: int64;

function DSiTimeGetTime64: int64;
begin
  Result := timeGetTime;
  if Result < GLastTimeGetTime then
    GTimeGetTimeBase := GTimeGetTimeBase + $100000000;
  GLastTimeGetTime := Result;
  Result := Result + GTimeGetTimeBase;
end; { DSiTimeGetTime64 }

Vous pouvez utiliser le type de données pour éviter tout débordement Int64:

var
  Start, Delta : Int64;
begin
  Start := GetTickCount;
  ...
  Delta := GetTickCount - start;
  if (Delta > 10000) then
    ...
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top