我正在寻找一种等待多个条件变量的方法。 即。类似的东西:
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();
}
条件变量可能是这样的。如果没有替代解决方案?
由于
答案 0 :(得分:11)
我不相信你可以用boost :: thread做这样的事情。也许是因为POSIX条件变量不允许这种类型的构造。当然,Windows有一个WaitForMultipleObjects作为aJ发布,如果你愿意将你的代码限制为Windows同步原语,这可能是一个解决方案。
另一种选择是使用更少的条件变量:只有1个条件变量,当发生任何“有趣”时你会触发它。然后,在你想等待的任何时候,你运行一个循环来检查你的特定兴趣情况是否已经出现,如果没有,则返回等待条件变量。你应该在这样的循环中等待那些条件变量,因为条件变量等待受到虚假的唤醒(来自boost :: thread docs,强调我的):
void wait(boost::unique_lock<boost::mutex>& lock)
...
效果:
原子调用lock.unlock()
并阻止当前线程。通过调用this->notify_one()
或this->notify_all()
或虚假通知线程将取消阻止。 ...
答案 1 :(得分:11)
正如Managu已经回答的那样,您可以使用相同的条件变量并在while循环中检查多个“事件”(bool变量)。但是,必须使用condvar使用的相同互斥锁来保护对这些bool变量的并发访问。
由于我已经遇到了为相关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
}
答案 2 :(得分:0)
alternative solutions?
我不确定Boost库,但您可以使用WaitForMultipleObjects函数来等待多个内核对象。只需检查一下是否有帮助。
答案 3 :(得分:0)
正如Managu指出使用多种条件可能不是一个好的解决方案。您应该可以使用信号量来实现您想要做的事情。
答案 4 :(得分:0)
对多个事件使用相同的条件变量在技术上有效,但它不允许封装。所以我试图创建一个支持它的类。 尚未测试!此外,它不支持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();
// }