Вы правы в том, что вышеуказанная функция не дает четкого понимания всей картины. Ниже в основном все взаимодействующие функции, которые стремятся к ресурсам * this
Мне удалось сократить количество мьютексов до использования только 3. Но я думаю, что проблему практически невозможно решить с меньшим количеством мьютексов. Обязательным условием является то, что метод обновления должен быть как можно более дешевым.
У меня все еще есть один вопрос, касающийся исключения. Как вы можете видеть, вычислительный поток, выполняющий executeCalculations, может выдать исключение. Если есть некоторые потоки, ожидающие сигнала для продолжения, они просто не могут продолжать работу, так как даже возникли исключения. Возможно ли использование boost, чтобы каким-то образом разрешить бодрствующим потокам генерировать то же исключение, которое было брошено в сигнальный поток Если да, можете ли вы предоставить явный код, как работает идея?
Моему классу нужны следующие аттрибуты.
// state variables indicating is calculation necessary
mutable bool calculated_, frozen_;
// flag that tells waking threads to throw exceptions if
// LazyObject::performCalculations() threw any exceptions
mutable bool failed_;
// flag avoiding infinite recursion on single thread not recursively
// calling LazyObject::performCalculations() through recursive calls
// to LazyObject::calculate()
mutable bool calculating_;
// protects resources from simultaneous read & writes
mutable boost::mutex readWriteMutex_;
// protects that only one thread can simultaneously call calculate
//mutable boost::mutex waitMutex_;
mutable boost::recursive_try_mutex waitMutex_;
// mutex and semaphore for sleeping threads until calculate is ready
mutable boost::mutex condMutex_;
mutable boost::condition_variable condVariable_;
inline void LazyObject::performCalculations() {
// let derived classes specialize own implementation
}
inline void LazyObject::update() {
// observers don't expect notifications from frozen objects
// LazyObject forwards notifications only once until it has been
// recalculated
readWriteMutex_.lock();
calculated_ = false;
readWriteMutex_.unlock();
if (!frozen_) {
notifyObservers();
}
}
inline void LazyObject::recalculate() {
readWriteMutex_.lock();
bool wasFrozen = frozen_;
calculated_ = frozen_ = false;
try {
readWriteMutex_.unlock();
calculate();
} catch (...) {
readWriteMutex_.lock();
frozen_ = wasFrozen;
readWriteMutex_.unlock();
notifyObservers();
throw;
}
readWriteMutex_.lock();
frozen_ = wasFrozen;
readWriteMutex_.unlock();
notifyObservers();
}
inline void LazyObject::freeze() {
readWriteMutex_.lock();
frozen_ = true;
readWriteMutex_.unlock();
}
inline void LazyObject::unfreeze() {
readWriteMutex_.lock();
frozen_ = false;
readWriteMutex_.unlock();
// send notification, just in case we lost any
notifyObservers();
}
inline void LazyObject::calculate() const {
//boost::recursive_mutex::scoped_try_lock lock(waitMutex_);
readWriteMutex_.lock();
// see a snapshot of object's status
if (!calculated_ && !frozen_) {
if (waitMutex_.try_lock()) {
//recursive lock lets same thread pass, puts others on wait
if (calculating_) {
readWriteMutex_.unlock();
waitMutex_.unlock();
return;
} else {
calculating_ = true;
}
readWriteMutex_.unlock();
try {
performCalculations();
readWriteMutex_.lock();
calculating_ = false;
failed_ = false;
calculated_ = true;
readWriteMutex_.unlock();
waitMutex_.unlock();
condVariable_.notify_all();
return;
} catch (...) {
readWriteMutex_.lock();
calculating_ = false;
failed_ = true;
calculated_ = false;
readWriteMutex_.unlock();
waitMutex_.unlock();
condVariable_.notify_all();
throw;
}
} else {
// start a non blocking wait until calculation is ready
readWriteMutex_.unlock();
boost::mutex::scoped_lock lock(condMutex_);
condVariable_.wait(lock);
if (failed_)
throw std::exception();
else
return;
}
}
// no need to calculate
readWriteMutex_.unlock();
}