Question

Je cherche un moyen d'attendre plusieurs variables de condition. c'est à dire. quelque chose comme:

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();
}

Est-ce que cela est possible avec les variables de condition. Et sinon, existe-t-il des solutions alternatives?

Merci

Était-ce utile?

La solution

Je ne crois pas que vous puissiez faire une chose pareille avec boost :: thread. Peut-être parce que les variables de condition POSIX n'autorisent pas ce type de construction. Bien sûr, Windows a WaitForMultipleObjects comme jJ posté, ce qui pourrait constituer une solution si vous souhaitez restreindre votre code aux primitives de synchronisation Windows.

Une autre option consisterait à utiliser moins de variables de condition: vous n'avez qu'une variable de condition que vous déclenchez quand quelque chose & "intéressant &"; arrive. Ensuite, chaque fois que vous voulez attendre, vous exécutez une boucle qui vérifie si votre situation d’intérêt particulière a été relevée et, si ce n’est pas le cas, revenez à l’attente de la variable de condition. Vous devriez de toute façon attendre ces variables de condition dans une telle boucle, car les attentes des variables de condition sont sujettes à des réveils parasites (de boost :: thread docs, emphasis mine):

void wait(boost::unique_lock<boost::mutex>& lock)
...
Effets:
Appelez atomiquement lock.unlock() et bloque le thread actuel. Le thread se débloquera lorsqu'il sera averti par un appel à this->notify_one() ou this->notify_all(), ou à de manière infondée . ...

Autres conseils

Comme Managu a déjà répondu, vous pouvez utiliser la même variable de condition et rechercher plusieurs & "; événements &"; (variables bool) dans votre boucle while. Cependant, les accès simultanés à ces variables bool doivent être protégés à l'aide du même mutex que celui utilisé par condvar.

Depuis que j'ai déjà eu la peine de taper cet exemple de code pour un question , je vais la republier ici:

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
}
alternative solutions?

Je ne suis pas sûr de la bibliothèque Boost, mais vous pouvez utiliser WaitForMultipleObjects Fonction permettant d’attendre plusieurs objets du noyau. Il suffit de vérifier si cela aide.

Comme Managu le fait remarquer, l’utilisation de plusieurs conditions peut ne pas être une bonne solution en premier lieu. Ce que vous voulez faire devrait pouvoir être mis en oeuvre à l’aide de Semaphores.

L'utilisation de la même variable de condition pour plusieurs événements fonctionne techniquement, mais elle ne permet pas l'encapsulation. J'ai donc essayé de faire un cours qui le supporte. Pas encore testé! Par ailleurs, il ne prend pas en charge notify_one() car je n'ai pas encore trouvé comment le mettre en œuvre.

#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();
//  }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top