thread.h

Clase wxThreadHelper

La clase wxThreadHelper es una clase mix-in que gestiona un único subproceso en segundo plano, ya sea independiente o unible (véase wxThread para conocer las diferencias).

Al derivarse de wxThreadHelper, una clase puede implementar el código del subproceso en su propio método wxThreadHelper::Entry() y compartir fácilmente datos y objetos de sincronización entre el subproceso principal y el subproceso de trabajo.

Esto evita el incómodo paso de punteros que se necesita cuando el objeto original del subproceso principal tiene que sincronizarse con su subproceso de trabajo en su propio objeto derivado de wxThread.

Por ejemplo, wxFrame puede necesitar realizar algunos cálculos en un subproceso en segundo plano y luego mostrar los resultados de esos cálculos en la ventana principal.

Normalmente, se crearía un objeto derivado de wxThread con el código de cálculo implementado en wxThread::Entry. Para acceder a las entradas del cálculo, el objeto frame a menudo necesitaría pasar un puntero a sí mismo al objeto thread. Del mismo modo, el objeto frame mantendría un puntero al objeto thread.

Los datos compartidos y los objetos de sincronización podrían almacenarse en cualquiera de los dos objetos, aunque el objeto sin los datos tendría que acceder a ellos a través de un puntero. Sin embargo, con wxThreadHelper, el objeto marco y el objeto subproceso se tratan como el mismo objeto. Los datos compartidos y las variables de sincronización se almacenan en un único objeto, lo que elimina una capa de indirección y los punteros asociados.

Ejemplo:

class MyFrame : public wxFrame, public wxThreadHelper
{
public:
    MyFrame(...)
    {
        // También es posible utilizar tablas de eventos, pero el enlace dinámico es más sencillo.
        Bind(wxEVT_THREAD, &MyFrame::OnThreadUpdate, this);
    }
 
    ~MyFrame()
    {
        // Es mejor realizar cualquier limpieza de subprocesos en el 
        // controlador de eventos OnClose(), en lugar de en el destructor.
        // Esto se debe a que el bucle de eventos de una ventana de nivel 
        // superior ya no está activo cuando se llama a su destructor y, si el 
        // subproceso envía eventos al finalizar, estos no se procesarán a 
        // menos que se haya finalizado el subproceso desde OnClose.
        // Consultar @ref overview_windowdeletion para obtener más información.
    }
 
    ...
    void DoStartALongTask();
    void OnThreadUpdate(wxThreadEvent& evt);
    void OnClose(wxCloseEvent& evt);
    ...
 
protected:
    virtual wxThread::ExitCode Entry();
 
    // los datos de salida de la rutina Entry():
    char m_data[1024];
    wxCriticalSection m_dataCS; // protege el campo superior
 
    wxDECLARE_EVENT_TABLE();
};
 
wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
    EVT_CLOSE(MyFrame::OnClose)
wxEND_EVENT_TABLE()
 
void MyFrame::DoStartALongTask()
{
    // Queremos iniciar una tarea larga, pero no queremos que nuestra 
    // interfaz gráfica de usuario se bloquee mientras se ejecuta, por lo 
    // que utilizamos un subproceso para hacerlo.
    if (CreateThread(wxTHREAD_JOINABLE) != wxTHREAD_NO_ERROR)
    {
        wxLogError("¡No se pudo crear el subproceso de trabajo!");
        return;
    }
 
    // ¡Vamos!
    if (GetThread()->Run() != wxTHREAD_NO_ERROR)
    {
        wxLogError("¡No se pudo ejecutar el subproceso de trabajo!");
        return;
    }
}
 
wxThread::ExitCode MyFrame::Entry()
{
    // MUY IMPORTANTE: ¡esta función se ejecuta en el contexto del subproceso secundario!
    // No llamar a ninguna función GUI dentro de esta función; utilizar wxQueueEvent():
 
    int offset = 0;
 
    // Aquí realizamos nuestra larga tarea, llamando periódicamente a TestDestroy():
    while (!GetThread()->TestDestroy())
    {
        // Dado que Entry() se implementa en el contexto MyFrame, no necesitamos 
        // ningún puntero para acceder a las variables m_data y m_dataCS... ¡Genial!
 
        // Este es un ejemplo de la estructura genérica de un hilo de descarga:
        char buffer[1024];
        download_chunk(buffer, 1024);     // Esto lleva tiempo...
 
        {
            // Asegúrate de que nadie lea m_data mientras lo escribimos.
            wxCriticalSectionLocker lock(m_dataCS);
            memcpy(m_data+offset, buffer, 1024);
            offset += 1024;
        }
 
        // Indicar al hilo principal que la descarga ha finalizado.
        wxQueueEvent(GetEventHandler(), new wxThreadEvent());
    }
 
    // TestDestroy() devolvió verdadero (lo que significa que el hilo principal nos 
    // pidió que termináramos lo antes posible) o terminamos la tarea larga...
    return (wxThread::ExitCode)0;
}
 
void MyFrame::OnClose(wxCloseEvent&)
{
    // Importante: antes de finalizar, debemos esperar a que termine nuestro 
    // hilo unible, si está en ejecución; de hecho, utiliza variables de esta 
    // instancia y envía eventos a *this controlador de eventos.
 
    if (GetThread() &&      // Es posible que no se haya llamado a DoStartALongTask().
        GetThread()->IsRunning())
        GetThread()->Wait();
 
    Destroy();
}
 
void MyFrame::OnThreadUpdate(wxThreadEvent& evt)
{
    // ...hacer algo... p. ej., m_pGauge->Pulse();
 
    // Leer algunas partes de m_data sólo por diversión:
    wxCriticalSectionLocker lock(m_dataCS);
    wxPrintf("%c", m_data[100]);
}

Funciones miembro

wxThreadHelper()

wxThreadHelper::wxThreadHelper(wxThreadKind kind = wxTHREAD_JOINABLE)

Este constructor simplemente inicializa las variables miembro internas e indica a wxThreadHelper qué tipo debe tener el hilo gestionado internamente.

~wxThreadHelper()

virtual wxThreadHelper::~wxThreadHelper()

El destructor libera los recursos asociados con el hilo, forzando su terminación (utiliza la función wxThread::Kill).

Debido a la inseguridad de wxThread::Kill, siempre se debe esperar (con wxThread::Wait) a que los hilos unibles terminen o llamar a wxThread::Delete en los hilos separados, en lugar de confiar en este destructor para detener el hilo.

Create()

wxThreadError wxThreadHelper::Create(unsigned int stackSize = 0)

Obsoleto: Utilizar CreateThread() en su lugar.

CreateThread()

wxThreadError wxThreadHelper::CreateThread( wxThreadKind kind = wxTHREAD_JOINABLE, unsigned int stackSize = 0 )

Crea un nuevo hilo del tipo especificado.

El objeto hilo se crea en estado suspendido, y debe llamar a GetThread()->Run() para iniciar su ejecución.

Opcionalmente, se puede especificar el tamaño de la pila que se le asignará (se ignora en plataformas que no admiten su configuración explícita, por ejemplo, Unix).

Valor de retorno

Uno de los valores de la enumeración wxThreadError.

Entry()

virtual ExitCode wxThreadHelper::Entry()

Este es el punto de entrada del subproceso.

Esta función es puramente virtual y debe ser implementada por cualquier clase derivada. La ejecución del subproceso comenzará aquí.

Normalmente, se querrá que Entry() tenga el siguiente aspecto:

wxThread::ExitCode Entry()
{
    while (!GetThread()->TestDestroy())
    {
        // ... hacer algo de trabajo ...
 
        if (IsWorkCompleted)
            break;
 
        if (HappenedStoppingError)
            return (wxThread::ExitCode)1;   // fracaso
    }
 
    return (wxThread::ExitCode)0;           // éxito
}

El valor devuelto es el código de salida del subproceso, que sólo es útil para subprocesos unibles y es el valor devuelto por "GetThread()->Wait()".

Esta función es llamada por wxWidgets y nunca debe ser llamada directamente.

GetThread()

wxThread* wxThreadHelper::GetThread() const

Esta es una función pública que devuelve el objeto wxThread asociado con el hilo.

GetThreadKind()

wxThreadKind wxThreadHelper::GetThreadKind() const

Devuelve el último tipo de subproceso proporcionado a la función CreateThread() o al constructor.

OnDelete()

virtual void wxThreadHelper::OnDelete()

Callback llamado por Delete() antes de eliminar realmente el subproceso.

Esta función puede ser sobrescrita por la clase derivada para realizar alguna tarea específica cuando el subproceso se destruye correctamente. Hay que tener en cuenta que se ejecutará en el contexto del subproceso que llamó a Delete() y no en el contexto de este subproceso.

Hay que tener en cuenta que TestDestroy() se bloqueará hasta que OnDelete() devuelva un resultado, por lo que esta función debe devolver un resultado lo más rápido posible y, en ningún caso, debe realizar acciones de la interfaz gráfica de usuario ni intentar adquirir bloqueos, ya que esto podría provocar fácilmente un interbloqueo.

OnExit()

virtual void wxThreadHelper::OnExit()

Callback llamado por wxThread::Exit() antes de salir realmente del hilo.

Esta función no se llamará si el hilo se ha eliminado con wxThread::Kill.

Esta función puede ser sobrescrita por la clase derivada para realizar alguna tarea específica cuando se sale del hilo. La versión de la clase base no hace nada y no es necesario llamarla si se sobrescribe este método.

Hay que tener en cuenta que esta función está protegida desde wxWidgets 3.1.1, pero anteriormente existía como método privado desde la versión 2.9.2.

OnKill()

virtual void wxThreadHelper::OnKill()

Callback llamado por wxThread::Kill() antes de eliminar realmente el hilo.

Esta función puede ser sobrescrita por la clase derivada para realizar alguna tarea específica cuando se termina el hilo. Hay que tener en cuenta que se ejecutará en el contexto del hilo que llamó a wxThread::Kill() y no en el contexto de este hilo.