2017-01-13 3 views
2

У меня есть поток, который периодически вызывает функцию обратного вызова. В зависимости от состояния функция обратного вызова должна получить RwLock ресурса, совместно используемого другими потоками, и заблокировать доступ к ресурсу даже за пределами функции обратного вызова. Затем он снова в зависимости от состояния освобождает ресурс снова в более позднем цикле обратного вызова.Приобретение RwLock для чтения и сохранения его за пределами области

Моей идеей было положить в структуру, которая была бы None, когда ресурс не заблокирован и Some(RwLockReadGuard<T>), когда ресурс заблокирован.

К сожалению, я не могу выполнить эту работу. Я должен создать структуру, которая содержит вне потока функции обратного вызова. Несмотря на то, что в то время, когда структура перемещается в поток, Option является None, компилятор не позволит мне передать этот параметр, потому что the trait bound ``std::sync::RwLockReadGuard<'_, T>: std::marker::Send`` is not satisfied.

Возможно, какой-то код. Надеюсь, это достаточно объяснительно.

use std::thread; 
use std::sync::{Arc, RwLock, RwLockReadGuard}; 


struct Handler<'a> { 
     resource: Arc<RwLock<String>>, 
     locked_resource: Option<RwLockReadGuard<'a, String>>, 
     counter: usize, 
} 

impl<'a> Handler<'a> { 
     fn callback(&'a mut self) { 
       println!("Callback {}", self.counter); 
       if self.counter == 0 { 
         println!("Locking resource"); 
         let res = self.resource.read().unwrap(); 
         self.locked_resource = Some(res); 
       } 

       self.counter += 1; 

       if self.counter == 100 { 
         println!("Releasing resource"); 
         self.locked_resource = None; 
       } 

       if self.counter == 200 { 
         self.counter = 0; 
       } 
     } 
} 


fn main() { 
     let resource = Arc::new(RwLock::new("foo".to_string())); 

     let handler = Handler { 
       resource: resource.clone(), 
       locked_resource: None, 
       counter: 0 
     }; 

     // This gives E0277 
     let thread = thread::spawn(move || { 
       loop { 
         handler.callback(); 
       } 
     }); 
} 

ответ

3

Проблема заключается в том: запирания и отпирания необходимость произойти в том же потоке. Это, например, ограничение pthread.

К счастью, система типа Rust достаточно выразительна, чтобы моделировать это: делая RwLockReadGuard be !Send, он предотвращает случайное совместное использование замков! Радуйся!

Итак, вы можете заблокировать и разблокировать различные функции обратного вызова ... но на той же теме.

В вашем примере это так же просто, как перемещение создания handler внутри потока. В вашем реальном приложении это может быть немного сложнее, но будьте уверены: компилятор будет держать вас за руку;)

fn main() { 
    let resource = Arc::new(RwLock::new("foo".to_string())); 

    let thread = thread::spawn(move || { 
     let handler = Handler { 
       resource: resource, 
       locked_resource: None, 
       counter: 0 
     }; 

     loop { 
       handler.callback(); 
     } 
    }); 
} 
+0

Я подумал об этом и попробовал ваше решение. Но тогда он говорит, что «обработчик может не прожить достаточно долго». Но это не вариант, так как в моем реальном проекте я сам не настраиваю нить. Я просто передаю объект-признак с функцией обратного вызова, и библиотека запускает поток. –

+0

Интересная ошибка, которая не имеет ничего общего с вопросом 'Mutex': вы не можете [брать у брата] (http://stackoverflow.com/questions/32300132/why-cant-i-store-a-value-and -a ссылка к этой стоимости-в-же-структуры). Что касается вашей проблемы, извините, но это правила. Вы не можете блокировать нить, разблокировать из другого потока и ожидать, что все будет работать: вы * имеете *, чтобы проектировать свою программу по-другому. –

+0

Но это тот же поток, не так ли? Это один поток, который снова и снова вызывает callback(). Я хочу, чтобы он удерживал замок на нескольких из этих циклов. –