Подтвердить что ты не робот

Ожидая нескольких переменных условия в 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();
}

Что-то вроде этого возможно с переменными состояния. А если нет альтернативных решений?

Спасибо

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

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
}

Ответ 3

alternative solutions?

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

Ответ 4

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

Ответ 5

Использование той же переменной условия для нескольких событий технически работает, но не позволяет инкапсулировать. Поэтому я попытался создать класс, который его поддерживает. Не тестировалось! Также он не поддерживает 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() 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() 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();
//  }