Clase wxCondition
Las variables wxCondition corresponden a condiciones pthread o a objetos de eventos Win32.
Se pueden utilizar en una aplicación multihilo para esperar hasta que la condición dada se cumpla, lo que ocurre cuando la condición se señala.
Nota: Es preferible utilizar std::condition en lugar de esta clase en el nuevo código.
Por ejemplo, si un subproceso de trabajo está realizando una tarea larga y otro subproceso tiene que esperar hasta que termine, este último subproceso esperará en el objeto de condición y el subproceso de trabajo lo señalará al salir (este ejemplo no es perfecto porque, en este caso concreto, sería mucho mejor utilizar wxThread::Wait para el subproceso de trabajo, pero si hay varios subprocesos de trabajo, ya tiene mucho más sentido).
Hay que tener en cuenta que una llamada a wxCondition::Signal puede producirse antes de que el otro subproceso llame a wxCondition::Wait y, al igual que con las condiciones pthread, la señal se pierde, por lo que si se quiere asegurar no perderla, se debe mantener el mutex asociado a la condición inicialmente bloqueado y volver a bloquearlo antes de llamar a wxCondition::Signal. Por supuesto, esto significa que esta llamada se bloqueará hasta que otro subproceso llame a wxCondition::Wait.
Ejemplo
Este ejemplo muestra cómo un subproceso principal puede iniciar un subproceso secundario que comienza a ejecutarse y luego espera hasta que el subproceso principal le indique que continúe:
class MySignallingThread : public wxThread
{
public:
MySignallingThread(wxMutex *mutex, wxCondition *condition)
{
m_mutex = mutex;
m_condition = condition;
}
virtual ExitCode Entry()
{
... hacer nuestro trabajo ...
// Informar a los demás subprocesos que estamos a punto de terminar:
// ¡debemos bloquear primero el mutex o podríamos señalar la condición
// antes de que los subprocesos en espera comiencen a esperar en él!
wxMutexLocker lock(*m_mutex);
m_condition->Broadcast(); // igual que Signal() aquí: sólo un cliente en espera.
return 0;
}
private:
wxCondition *m_condition;
wxMutex *m_mutex;
};
int main()
{
wxMutex mutex;
wxCondition condition(mutex);
// El mutex debe estar inicialmente bloqueado.
mutex.Lock();
// crea y ejecuta el subproceso, pero hay que tener en cuenta que no se podrá salir
// (ni señalar su salida) antes de que desbloqueemos el mutex a continuación.
MySignallingThread *thread = new MySignallingThread(&mutex, &condition);
thread->Run();
// esperar a que finalice el subproceso: Wait() desbloquea de forma atómica
// el mutex, lo que permite que el subproceso continúe y comienza a esperar.
condition.Wait();
// Ahora podemos salir.
return 0;
}
Por supuesto, aquí sería mucho mejor utilizar simplemente un hilo unible y llamar a wxThread::Wait en él, pero este ejemplo ilustra la importancia de bloquear correctamente el mutex cuando se utiliza wxCondition.
Funciones miembro
wxCondition()
wxCondition::wxCondition(wxMutex & mutex)
~wxCondition()
wxCondition::~wxCondition()
Destruye el objeto wxCondition.
El destructor no es virtual, por lo que esta clase no debe utilizarse de forma polimórfica.
Broadcast()
wxCondError wxCondition::Broadcast()
Transmite a todos los subprocesos en espera, activándolos todos.
Hay que tener en cuenta que este método se puede invocar independientemente de si el mutex asociado a esta condición está bloqueado o no.
IsOk()
bool wxCondition::IsOk() const
Devuelve verdadero si el objeto se ha inicializado correctamente, falso si se ha producido un error.
Signal()
wxCondError wxCondition::Signal()
Indica al objeto que active como máximo un subproceso.
Si varios subprocesos están esperando la misma condición, no se define qué subproceso se activará exactamente. Si no hay subprocesos esperando, la señal se pierde y habrá que volver a indicar la condición para activar cualquier subproceso que pueda empezar a esperar más adelante.
Hay que tener en cuenta que este método se puede llamar independientemente de si el mutex asociado a esta condición está bloqueado o no.
Wait()
wxCondError wxCondition::Wait()
Espera hasta que se señale la condición.
Este método libera de forma atómica el bloqueo del mutex asociado a esta condición (por eso debe bloquearse antes de llamar a Wait()) y pone el subproceso en espera hasta que se llame a Signal() o Broadcast(). A continuación, vuelve a bloquear el mutex y regresa.
Hay que tener en cuenta que, incluso si se hubiera llamado a Signal() antes de Wait() sin despertar ningún subproceso, el subproceso seguiría esperando a otro, por lo que es importante asegurarse de que la condición se señalará después de Wait(), o el subproceso podría permanecer en espera indefinidamente.
Valor de retorno
Devuelve wxCOND_NO_ERROR si se realiza correctamente, otro valor si se produce un error.
Wait()
template<typename Functor > wxCondError wxCondition::Wait(const Functor & predicate)
Espera hasta que se señale la condición y la condición asociada sea verdadera.
Se trata de una sobrecarga de conveniencia que puede utilizarse para ignorar activaciones espurias mientras se espera a que una condición específica se haga verdadera.
Equivalente a
while ( !predicate() )
{
wxCondError e = Wait();
if ( e != wxCOND_NO_ERROR )
return e;
}
return wxCOND_NO_ERROR;
El predicado suele ser una lambda:
condvar.Wait([]{return value == 1;});
WaitTimeout()
wxCondError wxCondition::WaitTimeout(unsigned long milliseconds)
Espera hasta que se señale la condición o hasta que expire el tiempo de espera.
Este método es idéntico a Wait(), salvo que devuelve el código de retorno wxCOND_TIMEOUT tan pronto como expira el tiempo de espera especificado.
Parámetros
- milliseconds
- Tiempo de espera en milisegundos.
Valor de retorno
Devuelve wxCOND_NO_ERROR si se ha señalado la condición, wxCOND_TIMEOUT si el tiempo de espera ha expirado antes de que esto ocurriera u otro código de error de la enumeración wxCondError.