Sử dụng biến tình trạng tương tự cho nhiều sự kiện về mặt kỹ thuật công trình, nhưng nó doesn' t cho phép đóng gói. Vì vậy, tôi đã có một nỗ lực để làm cho một lớp học hỗ trợ nó. Chưa được thử nghiệm! Ngoài ra, nó không hỗ trợ notify_one()
vì tôi chưa tìm ra cách thực hiện điều đó.
#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();
// }
Dường như với tôi rằng có một biến điều kiện thứ ba cho "hoặc 1 hoặc 2 thay đổi" thực sự là cách tiếp cận tốt nhất. Một cách tiếp cận khác là chuyển đổi trạng thái chờ trong tình trạng chờ đợi trên các bộ mô tả tập tin bằng cách chọn và sử dụng các đường ống cho giao tiếp luồng. Vì câu hỏi này là chung chung (nó không giải thích những gì đang chờ đợi), nên không rõ là chọn sẽ tốt hơn. –
Vấn đề với việc sử dụng một biến điều kiện là đóng gói. Bạn không thể có bất kỳ chức năng nào như 'GoStyleChannel :: receive_but_break_if (điều kiện_variable & cv, hàm & pred)'. Nói cách khác, giả sử bạn có một số chức năng chặn đã sử dụng biến điều kiện. Bạn muốn nó chặn cho đến khi điều kiện xảy ra * hoặc * tình trạng của bạn xảy ra. Bạn không thể làm điều đó mà không có một 'std :: condition_variable toàn cục somethingHasHappenedSomewhere', điều này thực sự là crap. – Timmmm