Curso de C++ Builder


Programación con Hebras

La clase TThread



C++ Builder proporciona la clase abstracta TThread para la creación de hebras.

Para definir una hebra se debe crear un descendiente de TThread, para lo cual se ha de crear una clase derivada de TThread. Para facilitar el trabajo, C++Builder incluye un asistente que lo hace por nosotros.

Ejemplo: Crear una hebra

Abrimos el repositorio en su página New (al que se accede desde la opción del menú File->New...). Seleccionamos el elemento Thread Object. Denominamos THebraPelota a la clase derivada de TThread. Ya hemos creado nuestra primera hebra en una unidad independiente (que almacenaremos en los ficheros HPelota.cpp y HPelota.h).

La clase TThread es abstracta porque posee un método virtual puro denominado Execute(), que será el que rellenemos con el código asociado a la hebra.

Como sucede con cualquier otra clase, en una clase derivada de TThread se pueden añadir todos los miembros (propiedades y métodos) que se necesiten. Obligatoriamente sólo hay que implementar:

Inicialización de las hebras

La inicialización de una hebra se realiza en su constructor, donde se establecen los valores iniciales de cuantas propiedades sean necesarias.

Antes de invocar al constructor de una clase derivada de TThread, se invoca al constructor de su clase base (TThread):

__fastcall TThread(bool CreateSuspended);

Éste recibe como parámetro false si deseamos que la hebra se ejecute inmediatamente después de ser creada o true si queremos que quede suspendida inicialmente.

Dos características de las hebras conviene establecerlas en el constructor: su prioridad y cuándo debe liberarse la hebra.

Asignación de una Prioridad por Defecto

La prioridad de una hebra indica qué grado de preferencia tiene la hebra cuando el sistema operativo reparte el tiempo de CPU entre las distintas hebras que se estén ejecutando en el sistema.

Para indicar la prioridad del objeto de hebra hay que fijar el valor de la propiedad Priority:

Valor Prioridad
tpIdle Se ejecuta cuando el sistema está inactivo.
tpLowest Dos puntos por debajo del valor normal.
tpLower Un punto por debajo del valor normal.
tpNormal La hebra tiene la prioridad normal.
tpHigher Un punto por encima del valor normal.
tpHighest Dos puntos por encima del valor normal.
tpTimeCritical La hebra tiene la prioridad más alta.

Si se aumenta mucho la prioridad de una hebra que requiera muchos recursos (vg: CPU) puede ocurrir que las demás hebras de la aplicación (y del sistema) no lleguen a procesarse a la velocidad adecuada.

Ejemplo:

Si tenemos un proceso que consume una cantidad considerable de tiempo de CPU, puede ser interesante dar la posibilidad al usuario de cancelar el proceso durante su ejecución. Esto se puede conseguir creando una hebra con prioridad normal para ese proceso y otra hebra de alta prioridad que únicamente esté a la espera de que el usuario pulse un botón de cancelación. La hebra de alta prioridad no consumirá tiempo de CPU porque estará bloqueada pero, en el caso de que el usuario pulse el botón, responderá rápidamente.

Liberación de las hebras

A menudo las hebras de una aplicación se ejecutan una sola vez. Si este es el caso, lo más sencillo es dejar que el objeto hebra se libere a sí mismo, estableciendo la propiedad FreeOnTerminate a true.

Sin embargo, hay casos en que el objeto hebra representa una tarea que debe realizarse varias veces. Si la aplicación requiere varias instancias del mismo objeto hebra (para ejecutar la hebra varias veces), se puede aumentar el rendimiento de la aplicación almacenando las hebras en una caché para utilizarlas más adelante, en lugar de destruirlas y crearlas de nuevo cada vez. Para ello, basta con fijar el valor de la propiedad FreeOnTerminate a false. En este caso, será responsabilidad del programador la liberación de la hebra.

MUY IMPORTANTE:
  • Antes de liberar una hebra hay que asegurarse de que no se esté ejecutando (ya veremos cómo).
  • En la versión 6 de C++Builder, la implementación de algunas funciones que se suelen emplear para manipular hebras exige NO utilizar la propiedad FreeOnTerminate. Este es el caso de WaitFor, cuya utilidad veremos más adelante.

Ejemplo:

  • En HPelota.h:
    #include "ObjGraf.h"
    
    En la clase THebraPelota:
    ...
    public:
      TPelota *Pelota;
      __fastcall THebraPelota(TPaintBox *PaintBox);
      __fastcall ~THebraPelota(void);
    ...
    
  • En HPelota.cpp:
    #include <stdlib.h>
    
    ...
    
    __fastcall THebraPelota::THebraPelota 
             (TPaintBox *PaintBox)
             : TThread(false)
    {
      Priority        = tpNormal;
      FreeOnTerminate = true;
    
      Pelota = new TPelota (   // Objeto pelota...
        PaintBox,              // ...aletorio.
        (TColor)(random(0xFF)        |
                (random(0xFF)*0x100) |
                (random(0xFF)*0x10000)),
        random(PaintBox->Width),
        random(PaintBox->Height),
        random(20)+1,
        TDireccion((random(2)+1) | 
                   (random(2)+1)*4));
    }
    
    __fastcall THebraPelota::~THebraPelota(void)
    {
      delete Pelota;
    }
    


  • Índice de la sección