ожидание нескольких условных переменных в boost? - PullRequest
17 голосов
/ 11 сентября 2009

Я ищу способ ожидания нескольких условных переменных. то есть. что-то вроде:

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

Возможно ли что-то подобное с условными переменными. А если нет, то есть ли альтернативные решения?

Спасибо

Ответы [ 5 ]

11 голосов
/ 14 января 2010

Как уже ответил Манагу, вы можете использовать одну и ту же переменную условия и проверять наличие нескольких «событий» (переменных bool) в цикле while. Однако одновременный доступ к этим переменным bool должен быть защищен с использованием того же мьютекса, который использует condvar.

Поскольку я уже столкнулся с трудностями при наборе этого примера кода для связанного вопроса , я опубликую его здесь:

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
}
11 голосов
/ 11 сентября 2009

Я не верю, что вы можете сделать что-нибудь подобное с boost :: thread. Возможно, потому что переменные условия POSIX не допускают этот тип конструкции. Конечно, в Windows есть WaitForMultipleObjects в виде сообщения J, что может быть решением, если вы захотите ограничить свой код примитивами синхронизации Windows.

Другой вариант - использовать меньше условных переменных: просто есть 1 условная переменная, которую вы запускаете, когда происходит что-то «интересное». Затем в любое время, когда вы захотите подождать, вы запускаете цикл, который проверяет, возникла ли ваша конкретная интересующая вас ситуация, и если нет, переходите к ожиданию переменной условия. В любом случае вы должны ожидать этих условных переменных в таком цикле, так как ожидания условных переменных подвержены ложным пробуждениям (из boost :: thread docs, выделение мое):

void wait(boost::unique_lock<boost::mutex>& lock)
...
Эффекты:
Атомно вызывает lock.unlock() и блокирует текущий поток. Поток будет разблокирован, когда получит уведомление по вызову this->notify_one() или this->notify_all(), или spierious . ...

0 голосов
/ 08 февраля 2016

Технически работает одна и та же условная переменная для нескольких событий, но она не позволяет инкапсуляцию. Поэтому я попытался создать класс, который бы его поддерживал. Пока не тестировалось! Также он не поддерживает notify_one(), так как я не понял, как это реализовать.

#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();
//  }
0 голосов
/ 11 сентября 2009

Как указывает Манагу, использование нескольких условий может быть не очень хорошим решением. То, что вы хотите сделать, должно быть реализовано с помощью семафоров.

0 голосов
/ 11 сентября 2009
alternative solutions?

Я не уверен в библиотеке Boost, но вы можете использовать WaitForMultipleObjects Функция для ожидания нескольких объектов ядра. Просто проверьте, помогает ли это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...