2016-04-08 1 views
0

J'utilise une minuterie dans mon jeu, il est démarré à l'intérieur d'un singleton et a un rappel appelé chaque milliseconde. Cependant, parfois, il tourne deux fois plus vite. C'est vraiment rare, je ne l'ai vu que 2 ou 3 fois (plus de 10.000 builds, des milliers d'exécutions).Windows timer fonctionne parfois deux fois plus vite

C'est la (partie importante du) Code:

//local variable in the .cpp: 
static float milliseconds=0; 

//the "actual" variable used in the game 
long int MilliSecondTimer::milliseconds=0; 

void CALLBACK TimerProc(UINT wTimerID, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2) 
{ 
    milliseconds++; 
    MilliSecondTimer::GetInstance()->SetTime(int(milliseconds), int(guimseconds)); 
} 

MilliSecondTimer::MilliSecondTimer() 
{ 
    UINT TimerID = timeSetEvent(1, 0, TimerProc, 0, TIME_PERIODIC | TIME_CALLBACK_FUNCTION); 
} 

Est-ce donc que si la création du singleton prend plus de 1 msec, alors il sera appelé deux fois parce que la fonction de minuterie appel

MilliSecondTimer::GetInstance()->SetTime(...) 

?

Merci!

+0

[Pourquoi les API Multimedia Timer (timeSetEvent) ne sont-elles pas aussi précises que je le pensais?] (Https://blogs.msdn.microsoft.com/mediasdkstuff/2009/07/02/why-are-the-multimedia -timer-apis-timesetevent-not-exact-as-i-would-expect /) –

+0

Vous n'obtenez pas 1 msec sauf si vous * appelez * aussi timeBeginPeriod(). Ou un autre programme le fait pour vous, comme Chrome. –

+0

Ce code provient d'un moment où le minuteur de Windows avait seulement une granularité autour de 50-60 msec. Ce n'est pas le problème ici. – Valmond

Répondre

1

Cela sera difficile à reproduire car vous avez dit que cela ne se produisait que rarement. Mais je crois que votre pensée initiale pour la cause du problème pourrait être vraie. Dans la documentation MSDN de timeSetEvent il dit:

La minuterie multimédia fonctionne dans son propre thread

Cela signifie, comme vous l'avez dit, si le singleton prend plus de 1ms pour créer, le constructeur du singleton sera appelé deux fois dans ce cas. Le deuxième appel au constructeur peut résulter de l'appel MilliSecondTimer::GetInstance()->SetTime(...) dans la première fonction de rappel de minuterie car à ce stade le singleton n'est pas encore créé et MilliSecondTimer::GetInstance() appellera à nouveau le constructeur qui à son tour créera un autre objet minuteur. Comme chaque temporisateur s'exécute dans son propre thread, vous aurez maintenant deux temporisateurs travaillant sur (incrémenter) les mêmes données (float statique en millisecondes), ce qui explique pourquoi il fonctionne deux fois plus vite. Et si tel est le cas, alors puisque deux instances du singleton sont créées dans le processus mais que le pointeur d'instance singleton pointe finalement vers l'une d'entre elles, il peut également y avoir une fuite de mémoire.

+1

La minuterie multimédia ne fonctionne pas dans un thread. Il fonctionne dans un temps critique, et celui vient de l'interruption de la minuterie. Au retour, le fil s'en va. En outre, je ne pense pas qu'il pourrait y avoir une fuite de mémoire. – Valmond

+0

@Valmond Je pense que la documentation essaie de dire que la fonction de rappel pointée par lpTimeProc qui est appelée à l'expiration des événements périodiques sera appelée (c'est-à-dire exécutée) dans un thread différent de celui qui démarre un événement timer spécifié (dans ce cas le constructeur singleton MilliSecondTimer :: MilliSecondTimer()). C'est pourquoi je pensais que vous auriez deux fonctions de rappel dans deux threads incrémentant votre variable float millisecondes statiques, ce qui fait que ça fonctionne deux fois plus vite. –

+0

@Valmond concernant la fuite de mémoire si nous supposons que le problème est que la construction de l'objet singleton prend plus de 1ms alors votre première fonction de rappel entraînera un autre appel au constructeur de la classe singleton par ** MilliSecondTimer :: GetInstance () ** La fonction que je suppose essaiera de créer dynamiquement l'objet singleton et assignera le résultat au membre de données de pointeur d'instance de cette classe. Dans ce cas, vous avez un pointeur mais créé dynamiquement deux objets de la classe singleton. Quoi qu'il en soit, cela suppose que la classe singleton prend plus de 1ms à construire. –