2008-12-12 10 views
6

¿Hay problemas de bucle en un programa?¿El lazo cerrado es malo?

Tengo una aplicación que tiene dos hilos para un simulador de física de juegos. Un hilo de actualización de juego y un hilo de renderizado. El subproceso se acelera haciendo que el subproceso duerma durante algunos milisegundos (para lograr la velocidad de fotogramas que deseo) y el subproceso updateGame (que actualiza mis posiciones en objetos de juego basados ​​en algunas ecuaciones físicas) se redujo previamente con un sueño de 10 milisegundos .

Sin embargo, recientemente desentregulé el subproceso updateGame y la simulación del movimiento de mis objetos parece ser significativamente más realista ahora que he sacado ese 10ms sleep. ¿Es malo hacer un bucle en caliente o tener un circuito cerrado?

private class UpdateTask implements Runnable 
{ 
    private long previousTime = System.currentTimeMillis(); 
    private long currentTime = previousTime; 
    private long elapsedTime; 


    public void run() 
    { 
     while(true) 
     { 
     currentTime = System.currentTimeMillis(); 
     elapsedTime = (currentTime - previousTime); // elapsed time in seconds 


     updateGame(elapsedTime/1000f); 

      try { 
       Thread.currentThread().sleep(1); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 

     previousTime = currentTime; 
     } 
    } 
} 

En este ejemplo sólo estoy durmiendo durante 1 ms (y de mi entendimiento con la forma en milisegundos precisión y la función de apagado automático funciona esto es probablemente más como 5-10ms. Si duermo más que esto comienza a tener un impacto sobre la exactitud de mi detección de colisiones y modelo de la física.

¿es una mala práctica tener lazos estrechos o bucles con 1ms duerme en ellos? ¿hay algo más que debería hacer en su lugar?

+1

Debe utilizar 'Thread.sleep (...)' en lugar de 'Thread.currentThread(). Sueño (. ..) 'porque es un método estático; su código podría tentarlo a hacer 'someOtherThread.sleep (...)' que no duerma 'someOtherThread'. – newacct

Respuesta

9

leí un gran post sobre manera eficiente y eficaz ejecución de cálculos de física de bucle: Fix Your Timestep!

Cuando un juego se está ejecutando que suele ser la principal aplicación que el usuario le importa dando la vuelta a tan apretado no es tan grande de un acuerdo . Lo que realmente deberías hacer es programar tus actualizaciones. Debe saber por cuánto tiempo, en su tasa de fotogramas objetivo, que su marco tiene que ejecutar. Debes medir el tiempo que tomó tu fotograma y solo dormir por el tiempo que tu foto tomó menos ese tiempo de fotograma conocido. De esta forma, su sistema se sincronizará con una velocidad de fotogramas y no variará con la cantidad de tiempo que tarda su fotograma en renderizar.

Otra cosa es que no creo que Thread.sleep tiene una muy buena resolución, más de 5 milisegundos, es posible que desee buscar un temporizador más preciso disponible para Java.

+0

Tengo dos hilos, mi thread de UpdateGame y mi hilo de renderizado. Mi hilo de representación está bloqueado en un maxFrameRate como dices basado en su tiempo transcurrido. – mmcdole

+0

@joshperry, leyendo el artículo ahora – mmcdole

+0

Gran upvote para 'corregir su tiempo'. En la Edad Media, utilizamos técnicas similares para justificar el texto. –

2

es sólo "malo" si tiene un impacto adverso en otra cosa en su sistema. En lugar de dormir por 1 ms, puede bloquear una condición que actualización de los datos, con un mínimo de 1 ms. De esa manera, siempre duerme al menos 1 ms, y más si no hay nada que hacer.

2

Como Adam ha señalado en su respuesta, puede haber un impacto adverso en el rendimiento del sistema.

También he intentado hacer juegos de una manera muy similar (teniendo un renderizado y cálculos de movimiento en hilos separados) y he encontrado que al no tener el Thread.sleep hará que la aplicación Java tome una porción muy importante de la CPU hora.

Otra cosa a considerar es que el temporizador del sistema sí mismo. Como ya mencionó, aunque el método Thread.sleep tarda la cantidad de milisegundos en dormirse, la precisión depende (como se indica en las especificaciones de la API) del temporizador proporcionado por el sistema operativo. En el caso de los sistemas operativos basados ​​en Windows NT, el timer resolution is 10 milliseconds. (Ver también: System.currentTimeMillis vs System.nanoTime)

Sí, es cierto que tener el Thread.sleep tiene el potencial de disminuir el rendimiento de su aplicación, pero al no tenerla, puede hacer que la aplicación se dispare.

Supongo que la decisión se reduce a si la aplicación debe ocupar una porción significativa de la utilización del sistema, o para actuar bien y compartir el tiempo de CPU con las otras aplicaciones que se ejecutan en el sistema.

0

Para un juego, probablemente no. Solo asegúrate de que tu juego se detiene cuando las tareas cambian.

0

En este caso, realmente desearía usar Thread.yield(). Es posible que un subproceso se ejecute continuamente y no permita que se ejecute ningún otro subproceso. Al hacer una llamada de rendimiento al final de cada iteración, el programador da una pista de que es el momento de permitir que otros subprocesos también se ejecuten.

1

Considere también los usuarios de computadoras portátiles, ejecutando un circuito cerrado continuamente mantendrá la CPU funcionando con fuerza, y esto masticará la batería (muchos juegos flash son culpables de esto). Algo a considerar cuando se decide si estrangular sus bucles o no.

1

La respuesta de joshperry es más o menos lo que quiere, pero también hay algunas maneras de hacerlo. Si está usando múltiples hilos, también tiene que lidiar con el bloqueo, etc. Dependiendo de la arquitectura de su juego, eso puede no ser un gran problema. Por ejemplo, ¿haces un montón de bloqueo, hay un montón de mensajes que pasan entre hilos, etc. Si eres un juego tradicional, normalmente tienes un único bucle principal? Tengo una cola de objetos CMD (ejecutable si quieres, pero puede también ser más un bus de eventos como en la naturaleza) que se ejecutan continuamente hasta que la cola está vacía. El hilo luego espera hasta que se indique que hay un nuevo cmd en la cola. Para la mayoría de los juegos, esto suele ser suficiente. Entonces, la pregunta es cómo/cuándo se agregan los cmds. Uso un temporizador/programador (también tenga en cuenta los comentarios sobre la resolución de tiempo de Java) para agregar un cmd al bucle principal a la velocidad de fotogramas requerida. Esto tiene la ventaja de ser amable con las computadoras portátiles, etc. Al inicio también puede comparar el sistema para ver qué tan rápido se está ejecutando, y luego establecer una velocidad de fotogramas adecuada (es decir, comenzar con una base compatible y luego trabajar a un máximo) En cada tipo de cmd se puede usar una evaluación comparativa o el uso de sugerencias de rendimiento especificadas por el usuario (es decir, la cantidad de detalles de representación) (es decir, el evento cmn/evento de render scence examina los parámetros de rendimiento para obtener detalles, etc.). (Nota: los cmds no deben ser ejecutables, pueden ser más como un bus de eventos con oyentes que se invocan en el hilo principal).

Además, si una tarea quiere usar el manejador de múltiples hilos/núcleos para el cmd (si es un modelo de tipo de evento - me gusta personalmente el modelo de evento) es más fácil acceder a la información de estado compartido sin necesidad de singletons globales) puede generar múltiples tareas (por ejemplo, utilizando un grupo de subprocesos existente, para que el costo de los nuevos subprocesos no se aplique a cada cmd) y luego utilice una clase de tipo de barrera para esperar a que se completen todas las tareas. Este método generalmente facilita el bloqueo, ya que cada cmd (o sistema) generalmente tiene diferentes requisitos de bloqueo. Por lo tanto, puede implementar solo el bloqueo para ese sistema y no tener que preocuparse por el bloqueo entre los subsistemas, es decir. para la física puedes bloquear paquetes de objetos en el área de juego, y cada hilo bifurcado en el grupo de subprocesos solo se preocupa por sus objetos, es decir. thread1 maneja objetos1 a 20, thread2 objetos 21-40, etc. (esto es solo para ilustrar el concepto de cómo cada cmd puede implementar un algoritmo de bloqueo personalizado que funcione mejor para lo que está haciendo, sin tener que preocuparse de lo que otros subsistemas están haciendo con datos de estado compartidos).

Lo importante es mirar cómo y por qué está utilizando hilos y de bloqueo, etc.

Cuestiones relacionadas