Hilos

Esto es una primera aproximación al uso de hilos en Windows. El tema es mucho más extenso y complejo de lo que se explica aquí, pero daremos algunas nociones para poder usarlos en nuestros programas y más concretamente, en juegos para la consola de Windows.

Para empezar, supongo que sabemos que Windows es un sistema operativo multitarea. Esto quiere decir que el sistema es capaz de gestionar los recursos de modo que varios procesos se puedan ejecutar al mismo tiempo.

Otros sistemas operativos actuales también son multitarea, como Linux. Sin embargo, las funciones que explicaremos aquí son exclusivas del API de Windows, aunque no así los conceptos como thread o hilo y mutex, etc. En otros artículos seguramente expliquemos cómo usar estas técnicas aplicadas a otros sistemas operativos.

En realidad, el número de procesos que se pueden ejecutar simultáneamente es limitado, y mucho. Concretamente, el límite lo pone el número de procesadores. Con un procesador de dos núcleos se pueden ejecutar dos tareas simultáneas, con uno de cuatro núcleos, cuatro, etc.

Sin embargo, el sistema puede simular la ejecución simultánea de muchas más tareas, dividiéndolas en pequeñas partes, y ejecutando secuencialmente partes de distintas tareas. Como el procesador es muy rápido, para el usuario los procesos parece que se ejecutan a la vez.

Generalmente tendemos a pensar que la multitarea se limita a poder tener abiertas varias ventanas con distintas aplicaciones a la vez, pero hay mucho más. Por ejemplo, un navegador como Firefox, puede tener abiertas varias páginas a la vez. A su vez, cada página, mientras se carga, puede estar descargando varios recursos: texto, imágenes, etc, y actualizando la pantalla a medida que los descarga.

En nuestros programas podemos hacer algo similar. Podemos crear hilos para realizar diferentes tareas, que no interfieran con el programa principal: comprobar el estado del teclado, mostrar la hora en pantalla, mover personajes distintos del jugador, etc.

Cada hilo es en realidad una función de nuestro programa, que se ejecuta en paralelo con él, y con la que podemos intercambiar información. Esto libera al programa principal de tareas de consulta y de esperas de eventos concretos.

La función CreateThread

Lo primero que tenemos que tener en cuenta es que un hilo es un recurso. Cada vez que creemos un hilo, el sistema creará un manipulador al que irá asociado el hilo, pero también reservará otros recursos, como una pila, memoria, etc. Es responsabilidad nuestra liberar esos recursos cuando ya no los necesitemos.

Para crear un hilo se usa la función CreateThread. Esta función devuelve un manipulador, un objeto de tipo HANDLE, que nos sirve para referirnos a ese hilo.

La función requiere seis parámetros, pero de momento sólo usaremos dos, el tercero y el cuarto. El tercero es la función que define el hilo, y el cuarto el parámetro que recibirá esa función al ser invocada. El resto de los parámetros pueden ser cero.

Otra cosa que necesitamos es, evidentemente, la función que debe ejecutar el hilo. Esa función debe tener el siguiente prototipo:

DWORD WINAPI ThreadFunction( void* );

El parámetro es un puntero genérico que podemos usar para pasar cualquier tipo de dato, ya que después podemos hacer un casting al tipo adecuado.

Bien, lo siguiente que necesitamos es el dato que pasaremos al hilo. Puede ser un puntero nulo, si el hilo no necesita parámetros. Puede ser un puntero a un tipo fundamental, o un puntero a una estructura o incluso a un objeto de una clase.

Para este ejemplo usaremos una entero.

int parametro;
HANDLE hHilo = CreateThread( NULL, 0, ThreadFunction, (void*)&parametro, 0, NULL);

En ese momento, el hilo empieza a ejecutarse.

El hilo debería disponer de algún mecanismo para terminar cuando queramos cerrar la aplicación. Esto es necesario porque no podremos destruir el manipulador del hilo mientras se siga ejecutando, y necesitamos destruir ese manipulador antes de salir de la aplicación.

En rigor, el sistema operativo libera esos recursos cuando el proceso que ha lanzado los hilos temina, pero según la documentación de Windows, se pueden producir pequeñas fugas de memoria.

Una vez termine el hilo, para liberar el manipulador usaremos la función CloseHandle.

CloseHandle(hHilo);

Primer ejemplo

Veamos un primer ejemplo sencillo. Crearemos un hilo que mostrará la hora en una esquina de la pantalla. Nuestra función para eso es esta:

DWORD WINAPI MostrarHora( void* par) {
    bool *salir = (bool*)par;  // Recuperar el parámetro, un puntero a bool
    SYSTEMTIME st;             // Estructura para obtener la hora
    COORD dwPos = {67,1};      // Coordenadas de pantalla para mostrar hora
    int seg = 0;               // Variable auxiliar para detectar cambio de segundo

    do {
        // Esperamos a que cambie el segundo:
        while(seg == st.wSecond) {
            GetLocalTime(&st);
            Sleep(100);
        }
        // Colocar el cursor:
        SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), dwPos);
        // Mostrar hora:
        printf("%02d:%02d:%02d", st.wHour, st.wMinute, st.wSecond);
        // Almacenar el segundo actual:
        seg = st.wSecond;
    } while(!*salir); // Repetir hasta que salir sea true

    printf("\nAbandonando el hilo\n");
    return 0;
}

En la función main creamos el hilo, que se seguirá ejecutando indefinidamente, hasta que pulsemos una tecla, momento en el que se modificará el valor de salir y el programa termina:

int main() {
    HANDLE hHora;
    bool salir = false;

    // Crear el hilo
    hHora = CreateThread( 0, 0, MostrarHora, (void*)&salir, 0, 0);

    // Esperar a que el usuario pulse una tecla, el hilo se sigue ejecutando:
    printf("Pulsa una tecla para salir");
    getchar();
    salir = true;

    // Dejar tiempo a que termine el hilo:
    Sleep(2000);

    // Liberar el manipulador:
    CloseHandle(hHora);
    return 0;
}

  Nombre Fichero Fecha Tamaño Contador Descarga
D Ejemplo 1 de hilos thread1.zip 2012-07-31 1300 bytes 186



suministrado por FreeFind
Valid HTML 4.0! Valid CSS!