stringtranslate.com

Ocupado esperando

En informática e ingeniería de software , la espera ocupada , el bucle ocupado o el giro es una técnica en la que un proceso verifica repetidamente si una condición es verdadera, como si hay una entrada de teclado o un bloqueo disponible. El giro también se puede utilizar para generar un retraso de tiempo arbitrario, una técnica que era necesaria en sistemas que carecían de un método para esperar un período de tiempo específico. Las velocidades de los procesadores varían mucho de una computadora a otra, especialmente porque algunos procesadores están diseñados para ajustar dinámicamente la velocidad según la carga de trabajo actual. [1] En consecuencia, el giro como técnica de retardo de tiempo puede producir resultados impredecibles o incluso inconsistentes en diferentes sistemas a menos que se incluya código para determinar el tiempo que tarda un procesador en ejecutar un bucle de "no hacer nada" , o que el código del bucle verifique explícitamente un bucle real. -reloj de tiempo .

En la mayoría de los casos, el giro se considera un antipatrón y debe evitarse, [2] ya que el tiempo del procesador que podría usarse para ejecutar una tarea diferente se desperdicia en actividades inútiles. El giro puede ser una estrategia válida en determinadas circunstancias, sobre todo en la implementación de spinlocks dentro de sistemas operativos diseñados para ejecutarse en sistemas SMP .

Ejemplo de código C

Los siguientes ejemplos de código C ilustran dos subprocesos que comparten un entero global i . El primer hilo utiliza la espera ocupada para comprobar si hay un cambio en el valor de i :

#incluir <pthread.h> #incluir <stdatomic.h> #incluir <stdio.h> #incluir <stdlib.h> #incluir <unistd.h>     /* i es global, por lo que es visible para todas las funciones. Hace uso del tipo especial * atomic_int, que permite accesos a la memoria atómica. */ atomic_int i = 0 ;   /* f1 usa un spinlock para esperar a que i cambie de 0. */ static void * f1 ( void * p ) { int local_i ; /* Cargar atómicamente el valor actual de i en local_i y comprobar si ese valor  es cero */ while (( local_i = atomic_load ( & i )) == 0 ) { /* no hacer nada - sólo seguir comprobando una y otra vez */ }                printf ( "el valor de i ha cambiado a %d. \n " , local_i ); devolver NULO ; }   vacío estático * f2 ( vacío * p ) { int local_i = 99 ; dormir ( 10 ); /* dormir durante 10 segundos */ atomic_store ( & i , local_i ); printf ( "t2 ha cambiado el valor de i a %d. \n " , local_i ); devolver NULO ; }               int principal () { int rc ; pthread_t t1 , t2 ;       rc = pthread_create ( & t1 , NULL , f1 , NULL ); if ( rc ! = 0 ) { fprintf ( stderr , "pthread f1 falló \n " ); devolver EXIT_FAILURE ; }                rc = pthread_create ( & t2 , NULL , f2 , NULL ); if ( rc ! = 0 ) { fprintf ( stderr , "pthread f2 falló \n " ); devolver EXIT_FAILURE ; }                pthread_join ( t1 , NULO ); pthread_join ( t2 , NULO ); puts ( "Todos los subprocesos terminados." ); devolver 0 ; }      

En un caso de uso como este, se puede considerar el uso de las variables de condición de C11 .

Alternativas

La mayoría de los sistemas operativos y bibliotecas de subprocesos proporcionan una variedad de llamadas al sistema que bloquearán el proceso en un evento, como adquisición de bloqueo, cambios de temporizador, disponibilidad de E/S o señales . El uso de este tipo de llamadas generalmente produce el resultado más simple, más eficiente, justo y libre de razas . Una sola llamada verifica, informa al planificador del evento que está esperando, inserta una barrera de memoria cuando corresponde y puede realizar una operación de E/S solicitada antes de regresar. Otros procesos pueden usar la CPU mientras la persona que llama está bloqueada. El planificador recibe la información necesaria para implementar la herencia de prioridad u otros mecanismos para evitar la inanición .

La espera de ocupado en sí misma puede reducirse mucho si se utiliza una función de retardo (por ejemplo, sleep()) que se encuentra en la mayoría de los sistemas operativos. Esto pone un hilo en suspensión durante un tiempo específico, durante el cual el hilo no perderá tiempo de CPU. Si el bucle comprueba algo simple, pasará la mayor parte del tiempo inactivo y desperdiciará muy poco tiempo de CPU.

En programas que nunca terminan (como los sistemas operativos), se puede implementar una espera de ocupación infinita mediante el uso de saltos incondicionales, como se muestra en esta sintaxis NASMjmp $ : . La CPU saltará incondicionalmente a su propia posición para siempre. Una espera ocupada como esta se puede reemplazar con:

dormir: hlt jmp dormir 

Para obtener más información, consulte HLT (instrucción x86) .

Uso apropiado

En la programación de bajo nivel, las esperas de ocupado pueden ser deseables. Puede que no sea deseable o práctico implementar un procesamiento impulsado por interrupciones para cada dispositivo de hardware, particularmente aquellos a los que rara vez se accede. A veces es necesario escribir algún tipo de datos de control en el hardware y luego recuperar el estado del dispositivo resultante de la operación de escritura, estado que puede no volverse válido hasta que hayan transcurrido varios ciclos de la máquina después de la escritura. El programador podría llamar a una función de retardo del sistema operativo, pero hacerlo puede consumir más tiempo del que se gastaría en girar durante unos pocos ciclos de reloj esperando que el dispositivo recupere su estado.

Ver también

Referencias

  1. ^ "Tecnología Intel Turbo Boost".
  2. ^ "Por qué no se debe utilizar la clase de tipo 'volátil'". Archivado desde el original el 4 de octubre de 2017 . Consultado el 10 de junio de 2013 .

enlaces externos