this_thread_interrupt_flag.clear_condition_variable();
}
};
};
void interruptible_wait(std::condition_variable& cv,
std::unique_lock
interruption_point();
this_thread_interrupt_flag.set_condition_variable(cv);
interrupt_flag::clear_cv_on_destruct guard;
interruption_point();
cv.wait_for(lk, std::chrono::milliseconds(1));
interruption_point();
}
Если мы ждем какой-то предикат, то таймаут продолжительностью 1 мс можно полностью скрыть внутри цикла проверки предиката:
template
void interruptible_wait(std::condition_variable& cv,
std::unique_lock
Predicate pred) {
interruption_point();
this_thread_interrupt_flag.set_condition_variable(cv);
interrupt_flag::clear_cv_on_destruct guard;
while (!this_thread_interrupt_flag.is_set() && !pred()) {
cv.wait_for(lk, std::chrono::milliseconds(1));
}
interruption_point();
}
Правда, предикат при этом проверяется чаще, чем необходимо, но зато эту функцию легко использовать вместо простого вызова wait()
. Легко реализовать и другие варианты функций с таймаутом, например: ждать в течение указанного времени или 1 мс в зависимости от того, что меньше.
Ну хорошо, с ожиданием std::condition_variable
мы разобрались, а что сказать о std::condition_variable_any
? Всё точно так же или можно сделать лучше?
9.2.4. Прерывание ожидания std::condition_variable_any
Класс std::condition_variable_any
отличается от std::condition_variable
тем, что работает с std::unique_lock
. Как выясняется, это сильно упрощает дело, так что мы сможем добиться более впечатляющих результатов, чем получилось с std::condition_variable
. Раз допустим set_clear_mutex
в классе interrupt_flag
, так и блокировку, переданную при вызове wait()
. Соответствующий код приведён в листинге ниже.
Листинг 9.12. Реализация interruptible_wait()
для std::condition_variable_any
class interrupt_flag {
std::atomic
std::condition_variable* thread_cond;
std::condition_variable_any* thread_cond_any;
std::mutex set_clear_mutex;
public:
interrupt_flag():
thread_cond(0), thread_cond_any(0) {}
void set() {
flag.store(true, std::memory_order_relaxed);
std::lock_guard
if (thread_cond) {
thread_cond->notify_all();
} else if (thread_cond_any) {
thread_cond_any->notify_all();
}
}
template
void wait(std::condition_variable_any& cv, Lockable& lk) {
struct custom_lock {
interrupt_flag* self;
Lockable& lk;
custom_lock(interrupt_flag* self_,
std::condition_variable_any& cond,
Lockable& lk_): self(self_), lk(lk_) {
self->set_clear_mutex.lock(); ←
(1)
self->thread_cond_any = &cond ←
(2)
}
void unlock() { ←
(3)
lk.unlock();
self->set_clear_mutex.unlock();
}
void lock() {
std::lock(self->set_clear_mutex, lk); ←
(4)
}
~custom_lock() {
self->thread_cond_any = 0; ←
(5)
self->set_clear_mutex.unlock();
}
};
custom_lock cl(this, cv, lk);
interruption_point();
cv.wait(cl);
interruption_point();
}
// остальное, как и раньше
};
template