2009-02-24 7 views
19

Ok, l'ho provato su un programma vuoto, e solo avere un po '(vero) {} in esecuzione mi ha dato> 50% della mia CPU. Ho un gioco su cui sto lavorando che usa un ciclo while come loop principale, e la CPU è sempre a 100.Come posso impedire a un ciclo while di Java di mangiare> il 50% della mia CPU?

Come posso ottenere Java per ripetere qualcosa più volte senza mangiare> 50% della mia CPU solo per ripetere?

+0

@William: Si prega di leggere la risposta di Mystere Man - è l'approccio più corretto, nonostante la risposta accettata in grande cambiamento. Dovresti basare il tuo gioco su un timer frame-rate e non su un loop CPU. –

+0

Inoltre, vorrei incoraggiare a cambiare la risposta accettata. –

risposta

40

Aggiungi un sonno di mettere il filo nella inattivo per un certo intervallo di:

Thread.sleep

Senza avere un sonno, il ciclo while consumerà tutte le risorse di calcolo che è disponibile. (. Ad esempio, teoricamente, 100% in un singolo sistema centrale, o il 50% in un dual core, e così via)

Per esempio, il seguente ciclo una volta attraverso un ciclo while circa ogni 50 millisecondi:

while (true) 
{ 
    try 
    { 
     Thread.sleep(50); 
    } 
    catch (Exception e) 
    { 
     e.printStackTrace(); 
    } 
} 

Ciò dovrebbe ridurre l'utilizzo della CPU piuttosto un po '.

Con una sospensione nel ciclo, il sistema operativo fornirà anche abbastanza tempo di sistema per altri thread e processi per fare le loro cose, quindi anche il sistema sarà reattivo. Ai tempi dei sistemi single core e dei sistemi operativi con scheduler non-così-buoni, cicli come questo avrebbero potuto rendere il sistema molto insensibile.


Dal momento che il tema di utilizzo di while anelli per un gioco si avvicinò, se il gioco sta per coinvolgere una GUI, il ciclo di gioco deve essere in un thread separato oppure la GUI stesso sarà non rispondere.

Se il programma sarà un gioco basato su console, il threading non sarà un problema, ma con interfacce utente grafiche gestite da eventi, con un ciclo di lunga durata nel codice renderà il GUI non risponde.

Il threading e simili sono aree di programmazione piuttosto complicate, soprattutto quando si inizia, quindi suggerisco di sollevare un'altra domanda quando è necessario.

Di seguito è riportato un esempio di un'applicazione Swing basata su JFrame che aggiorna uno JLabel che conterrà il valore restituito da System.currentTimeMillis. Il processo di aggiornamento avviene in un thread separato e un pulsante "Stop" interromperà il thread di aggiornamento.

Pochi concetti l'esempio chiarirà:

  • Un'applicazione GUI Swing-based con un thread separato per aggiornare il tempo - Questo consentirà di evitare lock up del thread GUI. (Chiamato EDT, o thread di invio evento in Swing.)
  • Avere il ciclo while con una condizione di loop che non è true, ma sostituito con uno boolean che determinerà se mantenere il ciclo attivo.
  • Come i fattori Thread.sleep in un'applicazione effettiva.

Ti prego di scusarmi per il lungo esempio

import java.awt.*; 
import java.awt.event.*; 
import javax.swing.*; 

public class TimeUpdate 
{ 
    public void makeGUI() 
    { 
     final JFrame f = new JFrame(); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     final JLabel l = new JLabel(); 

     class UpdateThread implements Runnable 
     { 
      // Boolean used to keep the update loop alive. 
      boolean running = true; 

      public void run() 
      { 
       // Typically want to have a way to get out of 
       // a loop. Setting running to false will 
       // stop the loop. 
       while (running) 
       { 
        try 
        { 
         l.setText("Time: " + 
           System.currentTimeMillis()); 

         Thread.sleep(50); 
        } 
        catch (InterruptedException e) 
        { 
         e.printStackTrace(); 
        } 
       } 

       // Once the run method exits, this thread 
       // will terminate. 
      } 
     } 

     // Start a new time update thread. 
     final UpdateThread t = new UpdateThread(); 
     new Thread(t).start(); 

     final JButton b = new JButton("Stop"); 
     b.addActionListener(new ActionListener() { 
      public void actionPerformed(ActionEvent e) 
      { 
       t.running = false; 
      } 
     }); 

     // Prepare the frame. 
     f.getContentPane().setLayout(new BorderLayout()); 
     f.getContentPane().add(l, BorderLayout.CENTER); 
     f.getContentPane().add(b, BorderLayout.SOUTH); 
     f.setLocation(100, 100); 
     f.pack(); 
     f.setVisible(true); 
    } 

    public static void main(String[] args) 
    { 
     SwingUtilities.invokeLater(new Runnable() 
     { 
      public void run() 
      { 
       new TimeUpdate().makeGUI(); 
      } 
     }); 
    } 
} 

Alcune risorse sulla filettatura e con swing:

+0

Questo è stato il mio primo pensiero, ma mi hai battuto :) – Pwninstein

+0

Ha funzionato perfettamente. Grazie! – William

+0

Nota che il polling (che dorme per un po 'di tempo) è perfettamente adatto al codice di esempio (aggiornando un'etichetta di testo con l'ora corrente). Ma per elaborare code di eventi complesse, è generalmente meglio bloccare la presenza di voci nella coda eventi (utilizzando un monitor) –

0

Sembra che il tuo thread sia in attesa. Vale a dire, è intrappolato in un ciclo in cui non fa nulla. In una situazione del genere, masticherà molti cicli della CPU senza alcun effetto.

Come menzionato da altri, Thread.sleep è la risposta.

28

Thread.Sleep potrebbe non essere la risposta completa. Per la maggior parte dei giochi, c'è troppa CPU per la quantità di lavoro necessaria. Dormire semplicemente per un determinato periodo di tempo non è poi così efficiente, poiché probabilmente continuerai a bruciare troppe risorse o non abbastanza. In genere vuoi temporeggiare il tuo lavoro in qualche modo.

Se ci pensate, lo schermo si aggiorna solo ad una certa velocità, tipicamente meno di 100 volte al secondo. Non ho familiarità con l'API Java per questo tipo di cose, ma quello che vuoi è scoprire la velocità di aggiornamento del monitor e quindi aggiornarlo solo una volta tra ogni aggiornamento.

Inoltre, non è necessario un ciclo come quello per il ciclo principale, è possibile utilizzare i timer per chiamare il codice di aggiornamento a intervalli regolari. Questo è ancora più efficiente.

10

Sei davvero occupato-in attesa, nel senso che stai macinando la tua CPU costantemente controllando una o più condizioni finché non sono vere.

Thread.sleep()non è la soluzione. L'utilizzo dei metodi wait() e notify() consente di eseguire solo ciò che si sta tentando, ma in modo molto più efficiente. Fondamentalmente, questo meccanismo consente ad un thread di dormire su un oggetto fino a quando l'oggetto decide che qualcosa è successo e vuole risvegliare tutti i thread che dormono su di esso.

exmaples codici possono essere trovati here e here.

Questo dovrebbe essere la vostra soluzione, e non nascondere il occupato-attendere con un timer.

+0

L'utilizzo di java.util.concurrent probabilmente andrà meglio. –

+0

+1, ma penso che un evento del timer sia migliore. –

0

Che ne dici di un programmatore basato su quarzo-molla che continua a ripetere il lavoro più e più volte a intervalli ripetuti.

5

Mentre sì, si potrebbe fare una

Thread.sleep(50) 

come la risposta accettata suggeriscono, si potrebbe anche chiamare

Thread.sleep(0) 

Questo dirà il processore di fare un cambio di contesto. Altri thread in attesa di essere eseguiti (come il thread di disegno della GUI) verranno quindi eseguiti e la macchina smetterà di rallentare.

Il modo sleep (0) ottimizzerà anche il tempo assegnato dal sistema operativo all'applicazione perché il thread tornerà immediatamente nella coda del processore (anziché attendere 50 ms prima di farlo), quindi se nessun altro thread è in attesa, il tuo thread continuerà ad essere eseguito.

+0

questo è esattamente ciò che Thread.yield() fa ... –

+0

la resa non è consigliata perché il suo comportamento non è standard a seconda della piattaforma. vedere http://www.javamex.com/tutorials/threads/yield.shtml –

0

Se c'è un interrupt è probabilmente per un motivo. Un modo migliore per gestirlo è

try { 
    while (true) { 
     Thread.sleep(50); 
    } 
} catch (InterruptException e) { 
    Thread.currentThread().interrupt(); 
} 

Tuttavia questo ciclo non fa nulla, quindi suggerisco di eliminarlo. È abbastanza raro che si desideri attendere una condizione (nel qual caso la classe Condition è una scelta migliore) in genere è possibile trasformare lo stesso comportamento per non aver bisogno di un ciclo.

Problemi correlati