2016-10-26 16 views
0

У меня есть общий Vec<CacheChange>. Всякий раз, когда написано новое CacheChange, я хочу пробудить читателей. Я помню, что Condvar хорош для сигнализации, когда предикат/ситуация готова, а именно, когда изменено Vec.Как отслеживать изменения с помощью Condvar и Mutex

Так что я потратил некоторое время на создание абстракции для Vec и предоставил wait и lock семантику.

Проблема в том, что я не знаю, когда следует сбросить Condvar. Каков хороший способ дать разумное количество времени читателям, чтобы поразить предикат и поработать над тем, чтобы держать замок? перед закрытием condvar? Я подхожу к Condvar с неправильным способом?

Это код ржавчины, но это больше вопрос об основах для точного параллельного доступа/уведомления между несколькими читателями.

pub struct Monitor<T>(
    sync::Arc<MonitorInner<T>> 
); 

struct MonitorInner<T> { 
    data: sync::Mutex<T>, 
    predicate: (sync::Mutex<bool>, sync::Condvar) 
} 

impl<T> Monitor<T> { 
    pub fn wait(&self) -> Result<(),sync::PoisonError<sync::MutexGuard<bool>>> { 
     let mut open = try!(self.0.predicate.0.lock()); 
     while !*open { 
      open = try!(self.0.predicate.1.wait(open)); 
     } 
     Ok(()) 
    } 

    pub fn lock(&self) -> Result<sync::MutexGuard<T>, sync::PoisonError<sync::MutexGuard<T>>> { 
     self.0.data.lock() 
    } 

    pub fn reset(&mut self) -> Result<(),sync::PoisonError<sync::MutexGuard<bool>>> { 
     let mut open = try!(self.0.predicate.0.lock()); 
     *open = false; 
     Ok(()) 
    } 

    pub fn wakeup_all(&mut self) -> Result<(),sync::PoisonError<sync::MutexGuard<bool>>> { 
     let mut open = try!(self.0.predicate.0.lock()); 
     *open = true; 
     self.0.predicate.1.notify_all(); 
     Ok(()) 
    } 
} 

После первого пробуждения мои читатели могут пропустить чтение. Вероятно, потому что они все еще держат блокировку данных, пока предикат снова был переключен. Я видел это в своем тестовом коде только с одним читателем и с одним автором.

Тогда возникает сложность, когда необходимо сбросить Monitor, в идеале это будет заблокировано после того, как у всех читателей будет возможность посмотреть данные. Это может вызвать проблемы с блокировкой, если читатель игнорирует их мониторы (нет гарантии, что они должны обслуживать каждый звонок для пробуждения).

Должен ли я использовать какую-то систему слежения за читателем с тайм-аутами и отслеживать при поступлении новых данных во время просмотра мониторов? Существует ли существующая парадигма, о которой я должен знать?

ответ

3

Проще всего использовать счетчик вместо булева.

struct MonitorInner<T> { 
    data: sync::Mutex<T>, 
    signal: sync::Condvar, 
    counter: sync::AtomicUsize, 
} 

Затем каждый раз, когда выполняется обновление, счетчик увеличивается. Он никогда не перезагружается, поэтому нет вопросов о том, когда нужно перезагрузить.

Конечно, это означает, что читатели должны помнить значение счетчика в последний раз, когда они проснулись.

+0

* Он никогда не сбрасывается *, ну и каждые 2^32 или 2^64 приращения. AKA 'time_t' 2038 issue^_^ – Shepmaster

+0

@Shepmaster: Да, я хочу, чтобы« AtomicU64 »был стабильным, поскольку 64-разрядный счетчик не может физически переполняться с 1-инкрементами (для математического наклона требуется ~ 600 лет для переполнения его 1 инк/нс или ~ 120 лет на частоте 5 ГГц). –

+0

Я мог бы поставить атомную нагрузку в 'wait()', и поток проснется, когда перезагруженный счетчик будет выше. – xrl

 Смежные вопросы

  • Нет связанных вопросов^_^