2009-02-24 51 views
19

Bien, probé esto en un programa vacío, y el solo hecho de ejecutar un rato (verdadero) {} me dio> 50% en mi CPU. Tengo un juego en el que estoy trabajando que usa un ciclo while ya que es un ciclo principal, y su CPU está en 100 todo el tiempo.¿Cómo puedo detener un ciclo while de Java al comer> 50% de mi CPU?

¿Cómo puedo hacer que Java repita algo una y otra vez sin consumir más del 50% de mi CPU solo para repetir?

+0

@William: Por favor, lea la respuesta de Mystere Man: es el enfoque más correcto, a pesar de la respuesta aceptada altamente votada. Deberías basar tu juego en un temporizador de velocidad de fotogramas no en un bucle de CPU. –

+0

Además, recomendaría cambiar la respuesta aceptada. –

Respuesta

40

Añadir un sueño para poner el hilo en inactivo durante algún intervalo:

Thread.sleep

Sin tener un sueño, el bucle while se consumen todos los recursos de computación que está disponible. (. Por ejemplo, en teoría, de 100% en un sistema de un solo núcleo, o 50% en un doble núcleo, y así sucesivamente)

Por ejemplo, el siguiente ciclo una vez a través de un bucle while aproximadamente cada 50 milisegundos:

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

Esto debería reducir un poco la utilización de la CPU.

Con un sueño en el ciclo, el sistema operativo también dará suficiente tiempo al sistema para que otros procesos y subprocesos hagan sus cosas, por lo que el sistema también responderá. Ya en la época de los sistemas de núcleo único y los sistemas operativos con programadores no tan buenos, los bucles como este podrían haber hecho que el sistema no respondiera.


Dado que el tema de la utilización de while bucles para un juego subió, si el juego va a implicar una interfaz gráfica de usuario, el bucle de juego debe estar en un hilo separado o bien la interfaz gráfica de usuario en sí dejará de responder.

Si el programa va a ser un juego basado en consola, entonces el enhebrado no será un problema, pero con las interfaces gráficas de usuario que están basadas en eventos, tener un bucle de larga vida en el código hará que el GUI no responde.

Enhebrar y tales son áreas de programación bastante complicadas, especialmente al comenzar, por lo que sugiero que se plantee otra pregunta cuando sea necesario.

El siguiente es un ejemplo de una aplicación Swing basada en un JFrame que actualiza un JLabel que contendrá el valor devuelto desde System.currentTimeMillis. El proceso de actualización se lleva a cabo en un subproceso separado, y un botón "Detener" detendrá el subproceso de actualización.

Pocos conceptos del ejemplo ilustrará:

  • Una aplicación de interfaz gráfica de usuario basada en Swing con un hilo separado para actualizar el tiempo - esto evitará composición final de la rosca interfaz gráfica de usuario. (Llamado EDT, o subproceso de envío de eventos en Swing).
  • Tiene el bucle while con una condición de bucle que no es true, pero se sustituye por boolean que determinará si se debe mantener el bucle con vida.
  • Cómo Thread.sleep factores en una aplicación real.

Por favor, discúlpeme para el largo ejemplo:

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

Algunos recursos sobre el roscado y el uso de swing:

+0

Eso fue lo primero que pensé, pero me ganaste :) – Pwninstein

+0

Eso funcionó perfectamente. ¡Gracias! – William

+0

Tenga en cuenta que el sondeo (dormir durante cierto tiempo) está perfectamente bien para el código de ejemplo (actualizando una etiqueta de texto con la hora actual). Pero para procesar colas de eventos complejos, generalmente es mejor bloquear la presencia de entradas en la cola de eventos (usando un monitor) –

0

Parece que su hilo está ocupado esperando. Es decir, está atrapado en un bucle en el que no hace nada. En tal situación, masticará muchos ciclos de CPU sin ningún efecto.

Según lo mencionado por otros, Thread.sleep es la respuesta.

28

Thread.Sleep puede no ser la respuesta completa. Para la mayoría de los juegos, hay demasiada CPU por la cantidad de trabajo necesario. Simplemente dormir durante un tiempo determinado no es tan eficiente, ya que es probable que todavía quemes demasiados recursos o que no sea suficiente. Por lo general, desea cronometrar su trabajo de alguna manera.

Si lo piensas bien, la pantalla solo se actualiza a un ritmo determinado, generalmente menos de 100 veces por segundo. No estoy familiarizado con las API de Java para este tipo de cosas, pero lo que desea es averiguar la velocidad de actualización del monitor y luego actualizar solo una vez entre cada actualización.

Además, no necesita un bucle así para su bucle principal, puede usar temporizadores para llamar a su código de actualización en un intervalo regular. Esto es aún más eficiente.

10

De hecho, estás ocupado esperando, lo que significa que estás controlando constantemente tu CPU al comprobar una o más condiciones hasta que sean ciertas.

Thread.sleep()no es la solución. El uso de los métodos wait() y notify() le permite hacer exactamente lo que está intentando, pero de manera mucho más eficiente. Básicamente, este mecanismo permite que un hilo duerma en un objeto hasta que el objeto decide que algo ha sucedido y quiere despertar a todos los hilos que duermen en él.

exmaples Código se pueden encontrar here y here.

Este debe ser su solución, y sin ocultar su ocupada esperar con un temporizador.

+0

El uso de java.util.concurrent probablemente sea mejor. –

+0

+1, pero creo que un evento de temporizador es mejor. –

0

¿Qué tal un programador basado en Quartz-Spring que sigue haciendo el trabajo una y otra vez en intervalos repetidos.

5

Mientras que sí, se puede hacer un

Thread.sleep(50) 

como la respuesta aceptada sugieren, también se podría llamar

Thread.sleep(0) 

Esto le dirá al procesador para hacer un cambio de contexto. Otros subprocesos en espera de ejecución (como el hilo de dibujo de la GUI) se ejecutarán y la máquina dejará de funcionar lentamente.

El modo dormir (0) también maximizará el tiempo asignado por el sistema operativo a su aplicación porque el hilo inmediatamente volverá a la cola del procesador (en lugar de esperar 50ms antes), así que si no hay otro hilo esperando, tu hilo continuará siendo ejecutado.

+0

esto es exactamente lo que Thread.yield() hace ... –

+0

no se recomienda el rendimiento porque su comportamiento no es estándar según la plataforma. ver http://www.javamex.com/tutorials/threads/yield.shtml –

0

Si hay una interrupción, es probable que sea por algún motivo. Una mejor manera de manejar esto es

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

Sin embargo, este bucle no sirve de nada, por lo que le sugiero que simplemente lo elimine. Es bastante raro que desee esperar una condición (en cuyo caso la clase Condición es una mejor opción), generalmente puede transformar el mismo comportamiento para no necesitar un bucle.

Cuestiones relacionadas