J'ai un problème bizarre - j'espère que quelqu'un peut m'expliquer ce qui se passe et une solution de contournement possible. J'implémente un noyau Z80 en Java, et tente de le ralentir en utilisant un objet java.util.Timer dans un thread séparé.Précision de synchronisation Java sous Windows XP et Windows 7
La configuration de base est que j'ai un thread exécutant une boucle d'exécution, 50 fois par seconde. Dans cette boucle d'exécution, cependant, de nombreux cycles sont exécutés, puis wait() est appelée. Le thread Timer externe appellera notifyAll() sur l'objet Z80 toutes les 20ms, simulant une fréquence d'horloge PAL Sega Master System de 3,54 MHz (ish). La méthode que j'ai décrite ci-dessus fonctionne parfaitement sur Windows 7 (j'ai essayé deux machines) mais j'ai aussi essayé deux machines Windows XP et sur les deux, l'objet Timer semble être oversleeping d'environ 50%. Cela signifie qu'une seconde de temps d'émulation prend environ 1,5 seconde sur une machine Windows XP.
J'ai essayé d'utiliser Thread.sleep() au lieu d'un objet Timer, mais cela a exactement le même effet. Je réalise que la granularité du temps dans la plupart des OS n'est pas meilleure que 1ms, mais je peux supporter 999ms ou 1001ms au lieu de 1000ms. Ce que je ne peux pas supporter est 1562ms - Je ne comprends pas pourquoi ma méthode marche bien sur une version plus récente de Windows, mais pas la plus ancienne - j'ai étudié les périodes d'interruption et ainsi de suite, mais ne semble pas ont développé une solution de contournement.
Pourriez-vous s'il vous plaît dites-moi la cause de ce problème et une solution de contournement suggéré? Merci beaucoup.
Mise à jour: Voici le code complet pour une petite application que je construit pour montrer la même question:
import java.util.Timer;
import java.util.TimerTask;
public class WorkThread extends Thread
{
private Timer timerThread;
private WakeUpTask timerTask;
public WorkThread()
{
timerThread = new Timer();
timerTask = new WakeUpTask(this);
}
public void run()
{
timerThread.schedule(timerTask, 0, 20);
while (true)
{
long startTime = System.nanoTime();
for (int i = 0; i < 50; i++)
{
int a = 1 + 1;
goToSleep();
}
long timeTaken = (System.nanoTime() - startTime)/1000000;
System.out.println("Time taken this loop: " + timeTaken + " milliseconds");
}
}
synchronized public void goToSleep()
{
try
{
wait();
}
catch (InterruptedException e)
{
System.exit(0);
}
}
synchronized public void wakeUp()
{
notifyAll();
}
private class WakeUpTask extends TimerTask
{
private WorkThread w;
public WakeUpTask(WorkThread t)
{
w = t;
}
public void run()
{
w.wakeUp();
}
}
}
Toute la classe principale est le fait de créer et de lancer un de ces threads de travail. Sur Windows 7, ce code produit un temps d'environ 999 ms - 1000 ms, ce qui est totalement correct. Exécuter le même pot sur Windows XP produit cependant un temps d'environ 1562ms - 1566ms, et c'est sur deux machines XP séparées que j'ai testé cela. Ils exécutent tous Java 6 mise à jour 27.
Je trouve que ce problème se produit parce que le temporisateur dort pendant 20 ms (assez petite valeur) - si je bosse toutes les boucles d'exécution pendant une seule seconde dans wait wait() - notifierAll() cycle, cela produit le bon résultat - Je suis sûr que les gens qui voient ce que j'essaie de faire (émuler un Sega Master System à 50fps) verront comment ce n'est pas une solution - cela ne donnera pas temps de réponse interactif, en sautant 49 sur 50. Comme je le dis, Win7 s'en sort bien. Désolé si mon code est trop grand :-(
Question intéressante. Serait-il possible pour vous de fournir un extrait de code autonome qui présente ce comportement? – NPE
Je peux vous donner le code source de la minuterie si vous aimez? Je vous donnerais le lot mais le noyau Z80 est assez lourd ;-) – PhilPotter1987
@aix * "extrait de code autonome" * Le code auto-contenu peut être court, mais il ne peut pas (par définition) être un [ extrait] (http://en.wikipedia.org/wiki/Snippet_%28programming%29). Je recommande de poster un [SSCCE] (http://pscode.org/sscce.html). –