2009-09-11 10 views
17

Estoy buscando una manera de esperar por múltiples variables de condición. es decir. algo así como:esperando múltiples variables de condición en boost?

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 como esto es posible con las variables de condición. Y si no hay soluciones alternativas?

Gracias

Respuesta

11

No creo que puedas hacer algo como esto con boost :: thread. Tal vez porque las variables de condición POSIX no permiten este tipo de constructo. 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 las primitivas de sincronización de Windows.

Otra opción sería usar menos variables de condición: solo tiene 1 variable de condición que se activa cuando ocurre algo "interesante". Luego, cada vez que desee esperar, ejecuta un ciclo que verifica si su situación particular de interés ha surgido, y si no, vuelva a esperar la variable de condición. Usted debe estar esperando en esas variables de condición en un bucle tal de todos modos, como se espera estado variable está sujeta a activaciones falsas (de documentos impulso :: hilo, el énfasis es mío):

void wait(boost::unique_lock<boost::mutex>& lock)
...
Efectos:
Llamada atomicamente lock.unlock() y bloquea el hilo actual. El hilo se desbloqueará cuando se lo notifique mediante una llamada a this->notify_one() o this->notify_all(), o falsamente. ...

+4

Me parece que tener una tercera variable de condición para "cambiar 1 o 2" es de hecho el mejor enfoque. Otro enfoque consiste en convertir la condición de espera en esperar en los descriptores de archivos usando select y usar pipes para la comunicación de subprocesos. Dado que esta pregunta es genérica (no explica en qué se esperaban las cosas), no está claro que seleccionar será mejor. –

+0

El problema con el uso de una variable de condición es la encapsulación. No puede tener ninguna función como 'GoStyleChannel :: receive_but_break_if (condition_variable & cv, function & pred)'. En otras palabras, supongamos que tiene alguna función de bloqueo que ya usa una variable de condición. Desea que se bloquee hasta que suceda la condición * o * su condición suceda. No se puede hacer sin tener una 'estándar :: variable_condición_algo algoHapantada_en_salida_, que en realidad es una porquería. – Timmmm

0
alternative solutions? 

no estoy seguro de la biblioteca Boost pero se puede utilizar la función WaitForMultipleObjects que esperar varios objetos del núcleo. Solo verifica si esto ayuda.

+0

Sí, sé de WaitForMultipleObject. Estoy buscando el equivalente en impulso. Pero parece que impulsar no proporciona uno. Ver: http://lists.boost.org/Archives/boost/2004/12/77175.php –

0

Como señala Managu al usar múltiples condiciones, podría no ser una buena solución en primer lugar. Lo que desea hacer debe ser posible implementarlo utilizando Semaphores.

11

Como ya se ha respondido Managu, puede usar la misma variable de condición y buscar múltiples "eventos" (variables bool) en su ciclo while. Sin embargo, el acceso simultáneo a estas variables bool debe protegerse utilizando el mismo mutex que utiliza el condvar.

Puesto que ya pasó por la molestia de escribir este ejemplo de código para una relacionada question, voy a publicar de nuevo 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 
} 
+0

+1 esta es una buena solución – Alexey

0

Usando la misma condición variable para múltiples eventos works técnicamente, pero que doesn' t permite la encapsulación. Así que tuve un intento de hacer una clase que lo apoye. ¡No probado todavía! Tampoco es compatible con notify_one() ya que no he encontrado la forma de implementarlo.

#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(); 
// } 
Cuestiones relacionadas