2011-09-26 44 views
9

Tôi có vấn đề kỳ lạ - Tôi hy vọng một người nào đó có thể giải thích cho tôi điều gì đang xảy ra và cách giải quyết có thể xảy ra. Tôi đang thực hiện một lõi Z80 trong Java, và cố gắng làm chậm nó xuống, bằng cách sử dụng một đối tượng java.util.Timer trong một luồng riêng biệt.Độ chính xác thời gian Java trên Windows XP so với Windows 7

Thiết lập cơ bản là tôi có một luồng chạy vòng lặp thực thi, 50 lần mỗi giây. Trong vòng lặp thực hiện này, tuy nhiên nhiều chu kỳ được thực hiện, và sau đó wait() được gọi. Chủ đề Timer bên ngoài sẽ gọi notifyAll() trên đối tượng Z80 sau mỗi 20ms, mô phỏng tần số xung nhịp hệ thống PAL Sega Master là 3,54 MHz (ish).

Phương pháp tôi đã mô tả ở trên hoạt động hoàn hảo trên Windows 7 (đã thử hai máy) nhưng tôi cũng đã thử hai máy Windows XP và cả hai, đối tượng Bộ hẹn giờ dường như quá tải khoảng 50%. Điều này có nghĩa là một giây của thời gian mô phỏng thực sự mất khoảng 1,5 giây hoặc lâu hơn trên máy tính Windows XP.

Tôi đã thử sử dụng Thread.sleep() thay vì đối tượng Timer, nhưng điều này có cùng tác dụng chính xác. Tôi nhận ra độ chi tiết của thời gian trong hầu hết các hệ điều hành không tốt hơn 1ms, nhưng tôi có thể đặt lên với 999ms hoặc 1001ms thay vì 1000ms. Những gì tôi không thể đưa ra là 1562ms - Tôi chỉ không hiểu tại sao phương pháp của tôi hoạt động OK trên phiên bản Windows mới hơn, nhưng không phải là phiên bản cũ hơn - Tôi đã điều tra các khoảng thời gian ngắt quãng, v.v. đã phát triển cách giải quyết.

Có ai vui lòng cho tôi biết nguyên nhân của vấn đề này và giải pháp được đề xuất không? Cảm ơn nhiều.

Cập nhật: Đây là mã đầy đủ cho một ứng dụng nhỏ tôi đã xây dựng để hiển thị cùng một vấn đề:

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

Tất cả các lớp học chính không được tạo ra và bắt đầu một trong những chủ đề người lao động. Trên Windows 7, mã này tạo ra thời gian khoảng 999ms - 1000ms, điều này hoàn toàn ổn. Chạy cùng một lọ trên Windows XP tuy nhiên tạo ra một khoảng thời gian khoảng 1562ms - 1566ms, và đây là trên hai máy XP riêng biệt mà tôi đã thử nghiệm này. Tất cả chúng đều đang chạy bản cập nhật Java 6 27.

Tôi thấy vấn đề này đang xảy ra vì Timer đang ngủ 20ms (một giá trị nhỏ) - nếu tôi bung tất cả các vòng lặp thực hiện trong một giây chờ đợi chờ đợi() - thông báo choAll() chu kỳ, điều này tạo ra kết quả chính xác - Tôi chắc chắn rằng những người nhìn thấy những gì tôi đang cố gắng thực hiện (mô phỏng hệ thống Sega Master ở 50fps) sẽ thấy đây không phải là giải pháp. thời gian đáp ứng tương tác, bỏ qua 49 của mỗi 50. Như tôi đã nói, Win7 đối phó tốt với điều này. Xin lỗi nếu mã của tôi là :-(quá lớn

+0

Câu hỏi thú vị. Bạn có thể cung cấp đoạn mã khép kín thể hiện hành vi này không? – NPE

+0

Tôi có thể cung cấp cho bạn mã nguồn của Bộ hẹn giờ nếu bạn thích? Tôi muốn cung cấp cho bạn rất nhiều nhưng lõi Z80 đang hình thành khá nặng ;-) – PhilPotter1987

+1

@aix * "đoạn mã khép kín" * Mã tự chứa có thể ngắn, nhưng không thể (theo định nghĩa) là [ đoạn mã] (http://en.wikipedia.org/wiki/Snippet_%28programming%29). Tôi khuyên bạn nên đăng một [SSCCE] (http://pscode.org/sscce.html). –

Trả lời

5

Có ai vui lòng cho tôi biết nguyên nhân của sự cố này và giải pháp được đề xuất không?

Sự cố bạn thấy có thể phải làm với clock resolution. Một số hệ điều hành (Windows XP và trước đó) nổi tiếng vì ngủ quên và chậm chạp khi chờ/thông báo/ngủ (ngắt nói chung).Trong khi đó các hệ điều hành khác (mọi Linux mà tôi đã thấy) đều xuất sắc trong việc kiểm soát trở lại tại thời điểm gần như được chỉ định.

Giải pháp thay thế? Đối với thời lượng ngắn, hãy sử dụng chế độ chờ trực tiếp (vòng lặp bận). Đối với thời lượng dài, ngủ ít hơn thời gian bạn thực sự muốn và sau đó sống chờ phần còn lại.

+1

Cũng giống như để thêm, khi sử dụng vòng lặp, hãy sử dụng [System.nanoTime()] (http://download.oracle.com/javase/6/docs/api/java/lang/System.html#nanoTime()) thay vì hơn [System.currentTimeMillis()] (http://download.oracle.com/javase/6/docs/api/java/lang/System.html#currentTimeMillis()) vì currentTimeMillis() có cùng độ chi tiết như Thread. ngủ(). [Link] (http://stackoverflow.com/questions/351565/system-currenttimemillis-vs-system-nanotime) để thảo luận thêm. – prunge

+0

Có cách nào để đặt độ phân giải đồng hồ xuống 1ms trên XP không? Tôi đã ấn tượng rằng có - nhưng rõ ràng là tôi đã không quản lý để làm như vậy tôi có thể sai ;-) – PhilPotter1987

+0

Xin lỗi, không. Đó là một điều hệ điều hành. Một trong những cách ẩn chứa trong đó một chương trình Java có thể vô tình trở thành nền tảng cụ thể. –

2

tôi muốn bỏ qua những cánh TimerTask và chỉ sử dụng một vòng lặp bận rộn:

long sleepUntil = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(20); 
while (System.nanoTime() < sleepUntil) { 
    Thread.sleep(2); // catch of InterruptedException left out for brevity 
} 

Các phần nghìn giây chậm trễ hai cung cấp cho các máy chủ hệ điều hành rất nhiều thời gian để làm việc trên các công cụ khác (và bạn có thể đang ở trên một đa lõi anyway) Mã số chương trình còn lại đơn giản hơn rất nhiều

Nếu mã hóa cứng hai mili giây quá nhiều công cụ cùn, bạn có thể tính toán thời gian ngủ yêu cầu và sử dụng quá tải Thread.sleep(long, int).

+0

Cảm ơn Barend này - Tôi đã không xem xét điều này. Tôi đã thử nó và nó gần gũi hơn với phạm vi thời gian yêu cầu của tôi - vẫn chưa đủ. Tôi vẫn còn thiếu ngủ khoảng 90ms, ngay cả khi tính thời gian ngủ yêu cầu và sử dụng Thread.sleep (long, int). Như tôi đã nói, tôi không phiền bị mất một hoặc hai mili giây nhưng điều này là quá nhiều.Cảm ơn bạn đã đề xuất mặc dù - nó là một tốt và khi tôi nhận được từ công việc và thử này trên Windows 7 tôi không có nghi ngờ nó sẽ làm việc tốt - nó vẫn không trả lời tại sao vấn đề này tồn tại mặc dù. – PhilPotter1987

1

Bạn có thể đặt độ phân giải hẹn giờ trên Windows XP.

http://msdn.microsoft.com/en-us/library/windows/desktop/dd757624%28v=vs.85%29.aspx

Vì đây là một thiết lập trên toàn hệ thống, bạn có thể sử dụng một công cụ để thiết lập độ phân giải, do đó bạn có thể xác minh xem đây là vấn đề của bạn.

Hãy thử này và xem nếu nó giúp: http://www.lucashale.com/timer-resolution/

Bạn có thể thấy timings tốt hơn trên các phiên bản mới hơn của Windows bởi vì, theo mặc định, phiên bản mới hơn có thể có timings chặt chẽ hơn. Ngoài ra, nếu bạn đang chạy một ứng dụng như Windows Media Player, nó sẽ cải thiện độ phân giải hẹn giờ. Vì vậy, nếu bạn tình cờ nghe một số bản nhạc trong khi chạy trình mô phỏng, bạn có thể có được thời gian tuyệt vời.

+0

Cảm ơn vì điều này - giả lập của tôi được thiết kế đặc biệt để được nền tảng mặc dù, và do đó không thể dựa vào Windows cụ thể như thế này để có được độ phân giải thời gian tốt. Cuối cùng tôi đã sử dụng một thread ngủ, với mã để rơi trở lại một vòng lặp chờ đợi bận rộn nếu thời gian được ra bởi hơn 1ms trong mỗi 20ms. Dường như hoạt động khá tốt trên XP ngay bây giờ. – PhilPotter1987

+0

Phil - Điều đó nghe có vẻ giống như một giải pháp đa nền tảng tốt mà không cần phải viết mã trường hợp đặc biệt cho mỗi nền tảng. Tốt đẹp – dss539

Các vấn đề liên quan