c++ - tag - ¿Esperando múltiples variables de condición en impulso?
shopify liquid (5)
Como Managu señala que usar múltiples condiciones puede no ser una buena solución en primer lugar. Lo que quieres hacer debería ser posible implementarlo usando Semáforos.
Estoy buscando una manera de esperar para múltiples variables de condición. es decir. algo como:
boost::condition_variable cond1;
boost::condition_variable cond2;
void wait_for_data_to_process()
{
boost::unique_lock<boost::mutex> lock(mut);
wait_any(lock, cond1, cond2); //boost only provides cond1.wait(lock);
process_data();
}
Es algo así como posible con las variables de condición. ¿Y si no hay soluciones alternativas?
Gracias
Como Managu ya respondió, puede usar la misma variable de condición y verificar múltiples "eventos" (variables bool) en su bucle while. Sin embargo, el acceso simultáneo a estas variables bool debe protegerse utilizando el mismo mutex que utiliza el condvar.
Dado que ya tuve la molestia de escribir este ejemplo de código para una question relacionada, lo reenviaré aquí:
boost::condition_variable condvar;
boost::mutex mutex;
bool finished1 = false;
bool finished2 = false;
void longComputation1()
{
{
boost::lock_guard<boost::mutex> lock(mutex);
finished1 = false;
}
// Perform long computation
{
boost::lock_guard<boost::mutex> lock(mutex);
finished1 = true;
}
condvar.notify_one();
}
void longComputation2()
{
{
boost::lock_guard<boost::mutex> lock(mutex);
finished2 = false;
}
// Perform long computation
{
boost::lock_guard<boost::mutex> lock(mutex);
finished2 = true;
}
condvar.notify_one();
}
void somefunction()
{
// Wait for long computations to finish without "spinning"
boost::lock_guard<boost::mutex> lock(mutex);
while(!finished1 && !finished2)
{
condvar.wait(lock);
}
// Computations are finished
}
No creo que puedas hacer algo como esto con boost :: thread. Quizás porque las variables de condición POSIX no permiten este tipo de construcción. Por supuesto, Windows tiene WaitForMultipleObjects como aJ publicado, lo que podría ser una solución si está dispuesto a restringir su código a los primitivos de sincronización de Windows.
Otra opción sería utilizar menos variables de condición: solo tiene 1 variable de condición que se activa cuando ocurre algo "interesante". Luego, cada vez que quiera esperar, ejecute un bucle que verifica si ha surgido su situación particular de interés y, si no, vuelva a esperar la variable de condición. Debería estar esperando esas variables de condición en ese bucle de todos modos, ya que las esperas de la variable de condición están sujetas a activaciones falsas (de boost :: thread docs, énfasis mío):
void wait(boost::unique_lock<boost::mutex>& lock)
...
Efectos:
Atómicamente llame a lock.unlock()
y bloquea el hilo actual. El subproceso se desbloqueará cuando sea notificado por una llamada a this->notify_one()
o this->notify_all()
, o falsamente . ...
Usar la misma variable de condición para múltiples eventos técnicamente funciona, pero no permite la encapsulación. Así que tuve un intento de hacer una clase que lo apoya. ¡Aún no probado! Además, no es compatible con notify_one()
ya que no he descubierto cómo implementar eso.
#pragma once
#include <condition_variable>
#include <unordered_set>
// This is like a `condition_variable` but you can wait on multiple `multi_condition_variable`s.
// Internally it works by creating a new `condition_variable` for each `wait_any()` and registering
// it with the target `multi_condition_variable`s. When `notify_all()` is called, the main `condition_variable`
// is notified, as well as all the temporary `condition_variable`s created by `wait_any()`.
//
// There are two caveats:
//
// 1. You can''t call the destructor if any threads are `wait()`ing. This is difficult to get around but
// it is the same as `std::wait_condition` anyway.
//
// 2. There is no `notify_one()`. You can *almost* implement this, but the only way I could think to do
// it was to add an `atomic_int` that indicates the number of waits(). Unfortunately there is no way
// to atomically increment it, and then wait.
class multi_condition_variable
{
public:
multi_condition_variable()
{
}
// Note that it is only safe to invoke the destructor if no thread is waiting on this condition variable.
~multi_condition_variable()
{
}
// Notify all threads calling wait(), and all wait_any()''s that contain this instance.
void notify_all()
{
_condition.notify_all();
for (auto o : _others)
o->notify_all();
}
// Wait for notify_all to be called, or a spurious wake-up.
void wait(std::unique_lock<std::mutex>& loc)
{
_condition.wait(loc);
}
// Wait for any of the notify_all()''s in `cvs` to be called, or a spurious wakeup.
static void wait_any(std::unique_lock<std::mutex>& loc, std::vector<std::reference_wrapper<multi_condition_variable>> cvs)
{
std::condition_variable c;
for (multi_condition_variable& cv : cvs)
cv.addOther(&c);
c.wait(loc);
for (multi_condition_variable& cv : cvs)
cv.removeOther(&c);
}
private:
void addOther(std::condition_variable* cv)
{
std::lock_guard<std::mutex> lock(_othersMutex);
_others.insert(cv);
}
void removeOther(std::condition_variable* cv)
{
// Note that *this may have been destroyed at this point.
std::lock_guard<std::mutex> lock(_othersMutex);
_others.erase(cv);
}
// The condition variable.
std::condition_variable _condition;
// When notified, also notify these.
std::unordered_set<std::condition_variable*> _others;
// Mutex to protect access to _others.
std::mutex _othersMutex;
};
// Example use:
//
// multi_condition_variable cond1;
// multi_condition_variable cond2;
//
// void wait_for_data_to_process()
// {
// unique_lock<boost::mutex> lock(mut);
//
// multi_condition_variable::wait_any(lock, {cond1, cond2});
//
// process_data();
// }
alternative solutions?
No estoy seguro de la biblioteca de Boost pero puede usar la función WaitForMultipleObjects para esperar varios objetos del kernel. Solo verifica si esto ayuda.