Mam dziwny problem - mam nadzieję, że ktoś może mi wyjaśnić, co się dzieje i możliwe obejście problemu. Implementuję rdzeń Z80 w Javie i próbuję go spowolnić, używając obiektu java.util.Timer w osobnym wątku.Dokładność synchronizacji czasu Java w systemie Windows XP i Windows 7
Podstawową konfiguracją jest to, że mam jeden wątek z uruchomioną pętlą, 50 razy na sekundę. W obrębie tej pętli wykonania wykonuje się wiele cykli, a następnie wywoływane jest wait(). Zewnętrzny wątek Timer wywoła funkcję notifyAll() na obiekcie Z80 co 20ms, symulując częstotliwość taktowania systemu PAL Sega Master System równą 3,54 MHz (ish).
Opisana powyżej metoda działa doskonale w systemie Windows 7 (wypróbowano dwie maszyny), ale wypróbowałem również dwa komputery z systemem Windows XP i na obu z nich obiekt Timer wydaje się być zaspany o około 50%. Oznacza to, że jedna sekunda czasu emulacji trwa około 1,5 sekundy na komputerze z systemem Windows XP.
Próbowałem użyć Thread.sleep() zamiast obiektu Timer, ale ma to dokładnie taki sam efekt. Zdaję sobie sprawę, że czasowość w większości systemów operacyjnych nie jest lepsza niż 1ms, ale mogę wytrzymać 999ms lub 1001ms zamiast 1000ms. Nie mogę znieść 1562ms - po prostu nie rozumiem, dlaczego moja metoda działa poprawnie w nowszej wersji systemu Windows, ale nie starszej - badałem okresy przerwania itd., Ale nie wydaje mi się opracowali obejście.
Czy ktoś mógłby mi powiedzieć, jaka jest przyczyna tego problemu i sugerowane obejście tego problemu? Wielkie dzięki.
Aktualizacja: Oto pełny kod dla mniejszych app zbudowany, aby pokazać ten sam problem:
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();
}
}
}
wszystkie główne klasy nie jest stworzyć i uruchomić jeden z tych wątków roboczych. W systemie Windows 7 ten kod generuje czas około 999ms - 1000ms, co jest całkowicie w porządku. Uruchamianie tego samego słoika w systemie Windows XP daje jednak czas około 1562 ms - 1566 ms, a to jest na dwóch osobnych maszynach XP, które przetestowałem. Wszystkie są uruchomione Java 6 aktualizacja 27.
Uważam, że ten problem ma miejsce, ponieważ Timer śpi przez 20 ms (dość mała wartość) - jeśli przelałbym wszystkie pętle wykonywania na jedną sekundę w oczekiwanie oczekiwania() - cykl notifyAll(), to daje prawidłowy wynik - jestem pewien, że ludzie, którzy zobaczą, co próbuję zrobić (emulować system Sega Master przy 50 klatkach na sekundę) zobaczą, jak to nie jest rozwiązaniem - to nie da Interaktywny czas reakcji, pomijanie 49 na 50. Jak mówię, Win7 radzi sobie z tym dobrze. Przepraszam, jeśli mój kod jest zbyt duża :-(
Interesujące pytanie. Czy możliwe byłoby podanie niezależnego fragmentu kodu, który wykazuje takie zachowanie? – NPE
Mogę podać kod źródłowy zegara, jeśli chcesz? Dałbym ci dużo, ale rdzeń Z80 kształtuje się dość mocno ;-) – PhilPotter1987
@ aix * "samodzielny fragment kodu" * Samodzielny kod może być krótki, ale nie może (z definicji) być [ snippet] (http://en.wikipedia.org/wiki/Snippet_%28programming%29). Polecam opublikowanie [SSCCE] (http://pscode.org/sscce.html). –