Clase wxThread
Un hilo o subproceso es básicamente una ruta de ejecución a través de un programa.
Los subprocesos a veces se denominan procesos ligeros, pero la diferencia fundamental entre los subprocesos y los procesos es que los espacios de memoria de los diferentes procesos están separados, mientras que todos los subprocesos comparten el mismo espacio de direcciones.
Nota: Es preferible utilizar std::thread en lugar de esta clase en el nuevo código.
Aunque facilita mucho compartir datos comunes entre varios subprocesos, también hace que sea mucho más fácil cometer errores, por lo que se recomienda utilizar con precaución los objetos de sincronización, como los mutex (ver wxMutex) o las secciones críticas (ver wxCriticalSection). Además, no se deben crear objetos de subproceso globales, ya que asignan memoria en su constructor, lo que causará problemas al sistema de comprobación de memoria.
Tipos de wxThreads
Hay dos tipos de subprocesos en wxWidgets: separados y unibles, basados en la API de subprocesos POSIX. Esto difiere de la API Win32, donde todos los subprocesos son unibles.
wxThreads en wxWidgets utilizan el comportamiento separado. Los hilos separados se eliminan a sí mismos una vez que han finalizado, ya sea por sí mismos cuando completan el procesamiento o mediante una llamada a Delete(), por lo que deben crearse en el montón (mediante el operador new, por ejemplo).
Normalmente, se querrá almacenar las instancias que se asignen de los wxThreads separados, para poder llamar a funciones en ellos. Sin embargo, debido a su naturaleza, siempre se deberá utilizar una sección crítica cuando acceda a ellos:
// Declarar un nuevo tipo de evento, que será utilizado por nuestra clase MyThread:
wxDECLARE_EVENT(wxEVT_COMMAND_MYTHREAD_COMPLETED, wxThreadEvent);
wxDECLARE_EVENT(wxEVT_COMMAND_MYTHREAD_UPDATE, wxThreadEvent);
class MyFrame;
class MyThread : public wxThread
{
public:
MyThread(MyFrame *handler)
: wxThread(wxTHREAD_DETACHED)
{ m_pHandler = handler }
~MyThread();
protected:
virtual ExitCode Entry();
MyFrame *m_pHandler;
};
class MyFrame : public wxFrame
{
public:
...
~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 haya finalizado el subproceso desde OnClose.
// Consultar @ref overview_windowdeletion para obtener más información.
}
...
void DoStartThread();
void DoPauseThread();
// Una rutina de reanudación sería casi idéntica a DoPauseThread().
void DoResumeThread() { ... }
void OnThreadUpdate(wxThreadEvent&);
void OnThreadCompletion(wxThreadEvent&);
void OnClose(wxCloseEvent&);
protected:
MyThread *m_pThread;
wxCriticalSection m_pThreadCS; // protege el puntero m_pThread
friend class MyThread; // permitirle acceder a nuestro m_pThread.
wxDECLARE_EVENT_TABLE();
};
wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_CLOSE(MyFrame::OnClose)
EVT_MENU(Minimal_Start, MyFrame::DoStartThread)
EVT_COMMAND(wxID_ANY, wxEVT_COMMAND_MYTHREAD_UPDATE, MyFrame::OnThreadUpdate)
EVT_COMMAND(wxID_ANY, wxEVT_COMMAND_MYTHREAD_COMPLETED, MyFrame::OnThreadCompletion)
wxEND_EVENT_TABLE()
wxDEFINE_EVENT(wxEVT_COMMAND_MYTHREAD_COMPLETED, wxThreadEvent);
wxDEFINE_EVENT(wxEVT_COMMAND_MYTHREAD_UPDATE, wxThreadEvent);
void MyFrame::DoStartThread()
{
m_pThread = new MyThread(this);
if ( m_pThread->Run() != wxTHREAD_NO_ERROR )
{
wxLogError("Can't create the thread!");
delete m_pThread;
m_pThread = nullptr;
}
// Después de la llamada a wxThread::Run(), el puntero m_pThread
// es "inseguro":
// En cualquier momento, el subproceso puede dejar de existir (porque completa su trabajo).
// Para evitar punteros colgantes, OnThreadExit() establecerá m_pThread en nullptr cuando el subproceso muera.
}
wxThread::ExitCode MyThread::Entry()
{
while (!TestDestroy())
{
// ... hacer un poco de trabajo ...
wxQueueEvent(m_pHandler, new wxThreadEvent(wxEVT_COMMAND_MYTHREAD_UPDATE));
}
// Indica al controlador de eventos que este subproceso va a ser destruido.
// NOTA: aquí asumimos que el uso del puntero m_pHandler es seguro,
// (en este caso, esto lo garantiza el destructor MyFrame).
wxQueueEvent(m_pHandler, new wxThreadEvent(wxEVT_COMMAND_MYTHREAD_COMPLETED));
return (wxThread::ExitCode)0; // éxito
}
MyThread::~MyThread()
{
wxCriticalSectionLocker enter(m_pHandler->m_pThreadCS);
// El hilo se está destruyendo; asegúrate de no dejar punteros colgantes por ahí.
m_pHandler->m_pThread = nullptr;
}
void MyFrame::OnThreadCompletion(wxThreadEvent&)
{
wxMessageOutputDebug().Printf("MYFRAME: MyThread exited!\n");
}
void MyFrame::OnThreadUpdate(wxThreadEvent&)
{
wxMessageOutputDebug().Printf("MYFRAME: actualizar MyThread...\n");
}
void MyFrame::DoPauseThread()
{
// Cada vez que accedemos al puntero m_pThread debemos asegurarnos
// de que no se modifique mientras tanto; dado que sólo puede haber un
// único subproceso dentro de una sección crítica determinada en un
// momento dado, el siguiente código es seguro:
wxCriticalSectionLocker enter(m_pThreadCS);
if (m_pThread) // ¿El hilo todavía existe?
{
// Sin una sección crítica, una vez alcanzado este punto, puede ocurrir
// que el programador del sistema operativo ceda el control a la función
// MyThread::Entry(), que a su vez puede regresar (porque completa su
// trabajo) invalidando el puntero m_pThread.
if (m_pThread->Pause() != wxTHREAD_NO_ERROR )
wxLogError("¡No se puede pausar el hilo!");
}
}
void MyFrame::OnClose(wxCloseEvent&)
{
{
wxCriticalSectionLocker enter(m_pThreadCS);
if (m_pThread) // ¿El hilo todavía existe?
{
wxMessageOutputDebug().Printf("MYFRAME: Eliminar hilo");
if (m_pThread->Delete() != wxTHREAD_NO_ERROR )
wxLogError("¡No se puede eliminar el hilo!");
}
} // Salir de la sección crítica para dar al subproceso la posibilidad
// de entrar en su destructor (¡que está protegido con la sección
// crítica m_pThreadCS!).
while (1)
{
{ // ¿Se ejecutó la función ~MyThread()?
wxCriticalSectionLocker enter(m_pThreadCS);
if (!m_pThread) break;
}
// esperar a que finalice el subproceso
wxThread::This()->Sleep(1);
}
Destroy();
}
Para ver un ejemplo más detallado y completo, consultar "Ejemplo de subproceso". Para ver una forma más sencilla de compartir datos y objetos de sincronización entre el subproceso principal y el secundario, consultar wxThreadHelper.
Por el contrario, los subprocesos unibles no se eliminan por sí mismos cuando terminan de procesarse y, por lo tanto, es seguro crearlos en la pila. Los subprocesos unibles también permiten obtener el valor devuelto por Entry() a través de Wait(). Sin embargo, no se debe apresurar a crear todos los subprocesos unibles, ya que esto también tiene una desventaja: se debe esperar (Wait()) a un subproceso unible o los recursos del sistema utilizados por él nunca se liberarán, y también se debe eliminar el objeto wxThread correspondiente si no lo ha creado en la pila. Por el contrario, los subprocesos separados son del tipo "disparar y olvidar": sólo hay que iniciar un subproceso separado y este se terminará y se destruirá por sí mismo.
Eliminación de wxThread
Independientemente de si ha finalizado o no, debe llamar a Wait() en un subproceso unible para liberar su memoria, tal y como se describe en Tipos de wxThreads. Sise ha creado un subproceso unible en el montón, hay que recordar eliminarlo manualmente con el operador delete o medios similares, ya que sólo los subprocesos separados gestionan este tipo de administración de memoria.
Dado que los hilos separados se eliminan a sí mismos cuando terminan de procesar, se debe tener cuidado al llamar a una rutina en uno de ellos. Si se está seguro de que el hilo sigue ejecutándose y se desea finalizarlo, se puede llamar a Delete() para finalizarlo correctamente (lo que implica que el hilo se eliminará después de esa llamada a Delete()). Debe darse por sentado que nunca debe intentar eliminar un hilo separado con el operador delete o medios similares.
Como se ha mencionado, las funciones Wait() o Delete() intentan terminar correctamente un subproceso unible y un subproceso separado, respectivamente. Lo hacen esperando hasta que el subproceso en cuestión llame a TestDestroy() o finalice el procesamiento (es decir, regrese de wxThread::Entry).
Obviamente, si el subproceso llama a TestDestroy() y no finaliza, el subproceso que llamó a Wait() o Delete() se detendrá. Por eso es importante llamar a TestDestroy() en la rutina Entry() de los subprocesos con la mayor frecuencia posible y salir inmediatamente cuando devuelva verdadero.
Como último recurso, se puede finalizar el subproceso inmediatamente mediante Kill(). Sin embargo, se recomienda encarecidamente no hacerlo, ya que no libera los recursos asociados al objeto (aunque el objeto wxThread de los subprocesos separados seguirá eliminándose) y podría dejar la biblioteca de tiempo de ejecución C en un estado indefinido.
Llamadas wxWidgets en subprocesos secundarios
Todos los subprocesos que no sean el "subproceso principal de la aplicación" (el que ejecuta wxApp::OnInit() o el que ejecuta la función principal, por ejemplo) se consideran "subprocesos secundarios".
Las llamadas GUI, como las que se realizan a wxWindow o wxBitmap, no son seguras en absoluto en subprocesos secundarios y podrían terminar la aplicación prematuramente. Esto se debe a varias razones, entre ellas la API nativa subyacente y el hecho de que wxThread no ejecuta un bucle de eventos GUI similar al de otras API como MFC.
Una solución para algunos compilaciones wxWidgets es llamar a wxMutexGUIEnter() antes de cualquier llamada GUI y luego llamar a wxMutexGUILeave() después. Sin embargo, la forma recomendada es simplemente procesar las llamadas GUI en el subproceso principal a través de un evento publicado por wxQueueEvent(). Esto no implica que las llamadas a estas clases sean seguras para subprocesos, ya que la mayoría de las clases wxWidgets no lo son, incluida wxString.
No sondear un wxThread
Un problema común que experimentan los usuarios con wxThread es que, en su hilo principal, comprueban el hilo de vez en cuando para ver si ha finalizado mediante IsRunning(), sólo para descubrir que su aplicación ha tenido problemas porque el hilo está utilizando el comportamiento predeterminado (es decir, está separado) y ya se ha eliminado a sí mismo. Naturalmente, intentan utilizar subprocesos unibles en lugar del comportamiento anterior. Sin embargo, sondear un wxThread para saber cuándo ha finalizado es, en general, una mala idea; de hecho, se debe evitar llamar a una rutina en cualquier wxThread en ejecución si es posible. En su lugar, buscar una forma de notificar cuando el subproceso haya finalizado.
Por lo general, sólo es necesario notificarlo al hilo principal, en cuyo caso se puede enviar un evento a través de wxQueueEvent(). En el caso de los hilos secundarios, se puede llamar a una rutina de otra clase cuando el hilo esté a punto de completar el procesamiento y/o establecer el valor de una variable, posiblemente utilizando mutexes (véase wxMutex) y/u otros medios de sincronización si es necesario.
Tipos miembro
ExitCode
typedef void* wxThread::ExitCode
El tipo de retorno para las funciones de subproceso.
Funciones miembro
wxThread()
wxThread::wxThread(wxThreadKind kind = wxTHREAD_DETACHED)
Este constructor crea un nuevo objeto de subproceso C++ independiente (predeterminado) o unible.
No crea ni inicia la ejecución del subproceso real; para ello se debe utilizar el método Run().
Los valores posibles para los parámetros de tipo son:
- wxTHREAD_DETACHED: crea un subproceso independiente.
- wxTHREAD_JOINABLE: crea un subproceso unible.
~wxThread()
virtual wxThread::~wxThread()
El destructor libera los recursos asociados con el subproceso.
Hay que tener en cuenta que nunca se debe eliminar un subproceso separado; sólo se puede llamar a Delete() en él o esperar a que termine (y se autodestruya) por sí mismo.
Dado que los subprocesos separados se eliminan por sí mismos, sólo se pueden asignar en el montón. Los subprocesos unibles deben eliminarse explícitamente. Las funciones Delete() y Kill() no eliminarán el objeto de subproceso C++. También es seguro asignarlos en la pila.
Create()
wxThreadError wxThread::Create(unsigned int stackSize = 0)
Crea un nuevo subproceso.
El objeto subproceso se crea en estado suspendido, y debe llamarse a Run() para comenzar a ejecutarlo. Opcionalmente, se puede especificar el tamaño de la pila que se le asignará (se ignora en plataformas que no admiten configurarlo explícitamente, por ejemplo, sistemas Unix sin pthread_attr_setstacksize).
Si no se especifica el tamaño de la pila, se utiliza el valor predeterminado del sistema.
Nota: No es necesario llamar a este método desde la versión 2.9.5, ya que Run() creará el subproceso internamente. Sólo es necesario llamar a Create() si se necesita hacer algo con el subproceso (por ejemplo, pasar su ID a una biblioteca externa) antes de que se inicie.
Valor de retorno
Uno de los siguientes:
- wxTHREAD_NO_ERROR: sin errores.
- wxTHREAD_NO_RESOURCE: no hay recursos suficientes para crear el subproceso.
- wxTHREAD_NO_RUNNING: el subproceso ya se está ejecutando.
Delete()
wxThreadError wxThread::Delete( ExitCode * rc = nullptr, wxThreadWait waitMode = wxTHREAD_WAIT_DEFAULT )
La llamada a Delete() solicita la terminación de cualquier subproceso.
Hay que tener en cuenta que Delete() no detiene realmente el subproceso, sino que simplemente le pide que termine, por lo que sólo funcionará si el subproceso llama a TestDestroy() periódicamente. Para los subprocesos separados, Delete() devuelve inmediatamente, sin esperar a que el subproceso termine realmente, mientras que para los subprocesos unibles sí espera a que el subproceso termine y también puede devolver su código de salida en el argumento rc.
Parámetros
- rc
- Para subprocesos unibles, se rellena con el código de salida del subproceso si el retorno es satisfactorio, si no es nulo. Para subprocesos separados, este parámetro no se utiliza.
- waitMode
- Tal y como se describe en la documentación de wxThreadWait, se debe utilizar wxTHREAD_WAIT_BLOCK como modo de espera, aunque actualmente se utilice wxTHREAD_WAIT_YIELD por motivos de compatibilidad. Este parámetro es nuevo en wxWidgets 2.9.2.
Nota: Esta función funciona en un hilo unible, pero en ese caso hace que la función TestDestroy() del hilo devuelva verdadero y luego espera a que se complete (es decir, difiere de TestDestroy() porque le pide al hilo que termine antes de esperar).
Consultar "Eliminación de wxThread" para obtener una explicación más amplia de esta rutina.
Entry()
virtual ExitCode wxThread::Entry()
Este es el punto de entrada del hilo.
Esta función es puramente virtual y debe ser implementada por cualquier clase derivada. La ejecución del hilo comenzará aquí.
El valor devuelto es el código de salida del hilo, que sólo es útil para hilos unibles y es el valor devuelto por Wait(). Esta función es llamada por wxWidgets y nunca debe ser llamada directamente.
Exit()
void wxThread::Exit(ExitCode exitcode = 0)
Esta es una función protegida de la clase wxThread y, por lo tanto, sólo se puede llamar desde una clase derivada.
Además, sólo se puede llamar en el contexto de este subproceso, es decir, un subproceso sólo puede salir de sí mismo, no de otro subproceso.
Esta función terminará el subproceso del sistema operativo (es decir, detendrá la ruta de ejecución asociada) y también eliminará el objeto C++ asociado para los subprocesos separados. OnExit() se invocará justo antes de salir.
GetCPUCount()
static int wxThread::GetCPUCount()
Devuelve el número de CPU del sistema o -1 si el valor es desconocido.
En los sistemas multinúcleo, el valor devuelto suele ser el número total de núcleos, ya que el sistema operativo suele abstraer una única CPU de N núcleos como N núcleos diferentes.
GetCurrentId()
static wxThreadIdType wxThread::GetCurrentId()
Devuelve el ID de subproceso específico de la plataforma del subproceso actual como un valor largo.
Esto se puede utilizar para identificar de forma única los subprocesos, incluso si no son wxThreads.
GetId()
wxThreadIdType wxThread::GetId() const
Obtiene el identificador del subproceso: se trata de un número dependiente de la plataforma que identifica de forma única al subproceso en todo el sistema durante su existencia (es decir, los identificadores de subproceso pueden reutilizarse).
GetKind()
wxThreadKind wxThread::GetKind() const
Devuelve el tipo de subproceso tal y como se proporcionó en el constructor.
GetMainId()
static wxThreadIdType wxThread::GetMainId()
Devuelve el ID del subproceso principal.
GetPriority()
unsigned int wxThread::GetPriority() const
Obtiene la prioridad del subproceso, entre 0 (la más baja) y 100 (la más alta).
IsAlive()
bool wxThread::IsAlive() const
Devuelve verdadero si el hilo está activo (es decir, iniciado y sin terminar).
Hay que tener en cuenta que esta función sólo se puede utilizar de forma segura con hilos unibles, no con hilos separados, ya que estos últimos se eliminan a sí mismos y, por lo tanto, cuando el hilo real ya no está activo, no es posible llamar a esta función porque el objeto wxThread ya no existe.
IsDetached()
bool wxThread::IsDetached() const
Devuelve verdadero si el subproceso es del tipo separado, falso si es del tipo unible.
IsMain()
static bool wxThread::IsMain()
Devuelve verdadero si el subproceso de llamada es el subproceso principal de la aplicación.
El subproceso principal en el contexto de wxWidgets es aquel que inicializó la biblioteca.
IsPaused()
bool wxThread::IsPaused() const
Devuelve verdadero si el subproceso está en pausa.
IsRunning()
bool wxThread::IsRunning() const
Devuelve verdadero si el subproceso se está ejecutando.
Este método sólo se puede utilizar de forma segura para subprocesos que se pueden unir; consultar la observación en IsAlive().
Kill()
wxThreadError wxThread::Kill()
Termina inmediatamente el hilo de destino.
"Esta función es peligrosa y debe utilizarse con extrema precaución" (¡y no utilizarse en absoluto siempre que sea posible!). Los recursos asignados al hilo no se liberarán y el estado de la biblioteca de tiempo de ejecución C puede volverse inconsistente. Utilizar Delete() para hilos separados o Wait() para hilos unibles.
Para los subprocesos separados, Kill() también eliminará el objeto C++ asociado. Sin embargo, esto no ocurrirá con los subprocesos unibles, lo que significa que se tendrá que eliminar el objeto wxThread para evitar fugas de memoria.
En ninguno de los dos casos se llamará a OnExit() del subproceso que está muriendo, por lo que no se realizará ninguna limpieza específica del subproceso. Esta función sólo se puede llamar desde otro contexto de subproceso, es decir, un subproceso no puede eliminarse a sí mismo.
También es un error llamar a esta función para un hilo que no se está ejecutando o que está en pausa (en este último caso, el hilo se reanudará primero); si se hace, se devolverá un error wxTHREAD_NOT_RUNNING.
MSWGetHandle()
WXHANDLE wxThread::MSWGetHandle() const
Obtiene el identificador del subproceso nativo.
Este método sólo existe en wxMSW; utilizar GetId() en código portable.
Pause()
wxThreadError wxThread::Pause()
Suspende el subproceso.
En algunas implementaciones (Win32), el subproceso se suspende inmediatamente, mientras que en otras sólo se suspenderá cuando se llame a TestDestroy() por segunda vez (por lo tanto, si el subproceso no lo llama en absoluto, no se suspenderá).
Esta función sólo se puede llamar desde otro contexto de subproceso.
Resume()
wxThreadError wxThread::Resume()
Reanuda un subproceso suspendido por la llamada a Pause().
Esta función sólo se puede llamar desde otro contexto de subproceso.
Run()
wxThreadError wxThread::Run()
Inicia la ejecución del subproceso.
Hay que tener en cuenta que una vez que ejecuta Run() un subproceso separado, cualquier llamada a una función que realice en el puntero del subproceso (debe asignarse en el montón) es "insegura"; es decir, el subproceso puede haber finalizado en cualquier momento después de Run() y su puntero puede quedar colgado. Consultar Tipos de wxThreads para ver un ejemplo de manipulación segura de subprocesos separados.
Esta función sólo se puede llamar desde otro contexto de subproceso.
Por último,hay que tener en cuenta que una vez que un subproceso ha finalizado y su función Entry() devuelve un valor, no se puede volver a llamar a Run() en él (un assert fallará en las compilaciones de depuración o se devolverá wxTHREAD_RUNNING en las compilaciones de lanzamiento).
SetConcurrency()
static bool wxThread::SetConcurrency(size_t level)
Establece el nivel de concurrencia de subprocesos para este proceso.
Esto es, aproximadamente, el número de subprocesos que el sistema intenta programar para que se ejecuten en paralelo. Se puede utilizar el valor 0 para el nivel a fin de establecer el valor predeterminado.
Valor de retorno
true si se realiza correctamente o false en caso contrario (por ejemplo, si esta función no está implementada para esta plataforma; actualmente, todas excepto Solaris).
SetName()
bool wxThread::SetName(const wxString & name)
Establece un nombre interno para el subproceso, lo que permite al depurador mostrar el nombre junto con la lista de subprocesos, siempre que tanto el sistema operativo como el depurador lo admitan.
El nombre del subproceso también puede ser visible en la lista de procesos y en los volcados de memoria (también en las compilaciones de lanzamiento).
Esta función está protegida, ya que debe llamarse desde una clase derivada, en el contexto del subproceso derivado. Un buen lugar para llamar a esta función es al principio de la función Entry().
Para que el código sea portable, el nombre debe estar en ASCII. En Linux, la longitud se trunca a 15 caracteres, mientras que en otras plataformas el nombre puede ser más largo.
Valor de retorno
O bien:
- false: Fallo o implementación faltante para este sistema operativo.
- true: Éxito o fallo indetectable.
SetNameForCurrent()
static bool wxThread::SetNameForCurrent(const wxString & name)
Establece un nombre interno para el subproceso actual, que puede ser un wxThread o cualquier otro tipo de subproceso; por ejemplo, un std::thread.
Valor de retorno
Una de las siguientes opciones:
- false: Fallo o implementación inexistente para este sistema operativo.
- true: Éxito o fallo indetectable.
SetPriority()
void wxThread::SetPriority(unsigned int priority)
Establece la prioridad del subproceso, entre 0 (la más baja) y 100 (la más alta).
Además de los valores sin procesar en el rango 0..100, se pueden utilizar las siguientes constantes simbólicas:
- wxPRIORITY_MIN: 0
- wxPRIORITY_DEFAULT: 50
- wxPRIORITY_MAX: 100
Hay que tener en cuenta que, actualmente, esta función no está implementada cuando se utiliza la política de programación predeterminada (SCHED_OTHER) en sistemas POSIX.
Sleep()
static void wxThread::Sleep(unsigned long milliseconds)
Pausa la ejecución del hilo durante el tiempo especificado.
Es equivalente a wxMilliSleep().
TestDestroy()
virtual bool wxThread::TestDestroy()
El subproceso debe llamar a esta función periódicamente para garantizar que las llamadas a Pause() y Delete() funcionen.
Si devuelve true, el subproceso debe salir lo antes posible. Hay que tener en cuenta que, en algunas plataformas (POSIX), la implementación de Pause() también depende de que se llame a esta función, por lo que no llamarla impediría que funcionaran tanto la detención como la suspensión del subproceso.
This()
static wxThread* wxThread::This()
Devuelve el objeto de subproceso para el subproceso de llamada.
Se devuelve nullptr si el subproceso de llamada es el subproceso principal (GUI), pero se debe utilizar IsMain() para comprobar si el subproceso es realmente el principal, ya que también se puede devolver nullptr para el subproceso no creado con la clase wxThread. En términos generales, el valor de retorno para dicho subproceso es indefinido.
Wait()
ExitCode wxThread::Wait(wxThreadWait flags = wxTHREAD_WAIT_DEFAULT)
Espera a que finalice un subproceso unible y devuelve el valor que el subproceso devolvió desde Entry() o "(ExitCode)-1" en caso de error.
Hay que tener en cuenta que, a diferencia de Delete(), esta función no cancela el subproceso de ninguna manera, por lo que el autor de la llamada espera todo el tiempo que sea necesario hasta que el subproceso finalice.
Sólo se puede utilizar Wait() para subprocesos unibles (no separados).
Esta función sólo se puede llamar desde otro contexto de subproceso.
Parámetros
- flags
- Tal y como se describe en la documentación de wxThreadWait, se debe utilizar wxTHREAD_WAIT_BLOCK como modo de espera, aunque actualmente se utilice wxTHREAD_WAIT_YIELD por motivos de compatibilidad. Este parámetro es nuevo en wxWidgets 2.9.2.
Consultar Eliminación de wxThread para obtener una explicación más detallada de esta rutina.
Yield()
static void wxThread::Yield()
Cede el resto del intervalo de tiempo del subproceso al sistema, permitiendo que se ejecuten los demás subprocesos.
Hay que tener en cuenta que se desaconseja encarecidamente el uso de esta función, ya que en muchos casos indica una debilidad en el diseño de tu modelo de subprocesos (al igual que el uso de las funciones Sleep()).
Los subprocesos deben utilizar la CPU de manera eficiente, es decir, deben realizar su trabajo actual de manera eficiente y, tan pronto como lo hayan terminado, bloquearse en un evento de activación (wxCondition, wxMutex, select(), poll(), ...) que será señalado, por ejemplo, por otros subprocesos o por un dispositivo de usuario una vez que haya más trabajo disponible para el subproceso. El uso de Yield() o Sleep() indica un comportamiento de tipo sondeo, ya que estamos renunciando de forma imprecisa a nuestro intervalo de tiempo y esperando hasta que, más tarde, se nos reactive, momento en el que nos damos cuenta de que realmente no hay mucho que hacer y volvemos a utilizar Yield()...
La característica más crítica de Yield() es que es específica del sistema operativo: puede haber cambios en el programador que hagan que el subproceso no se active de nuevo relativamente pronto, sino muchos segundos más tarde, lo que provocaría enormes problemas de rendimiento para la aplicación.
Con un subproceso que se comporta bien y es eficiente en cuanto a la CPU, es probable que el sistema operativo se encargue adecuadamente de su reactivación en el momento en que sea necesario, mientras que con subprocesos no deterministas que utilizan Yield, todo es posible y el programador del sistema es libre de penalizarlos drásticamente, y este efecto empeora con el aumento de la carga del sistema debido a la menor disponibilidad de recursos de CPU libres. Para obtener más información, puede consultar varios debates sobre sched_yield del kernel de Linux.