【问题标题】:Acquiring a RwLock for read and keep it beyond the scope获取 RwLock 进行读取并将其保持在范围之外
【发布时间】:2017-05-29 05:53:05
【问题描述】:

我有一个定期调用回调函数的线程。根据状态,回调函数应获取与其他线程共享的资源的RwLock,并保持资源锁定,即使超出回调函数的范围。然后它会在稍后的回调周期中再次根据状态再次释放资源。

我的想法是将Option<RwLockReadGuard<T>> 放入一个结构中,当资源未锁定时为None,当资源锁定时为Some(RwLockReadGuard<T>)

很遗憾,我无法完成这项工作。我必须在回调函数的线程之外设置包含Option<RwLockReadGuard<T>> 的结构。即使在将结构移入线程时OptionNone,编译器也不会让我通过该选项,因为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();
                }
        });
}

【问题讨论】:

    标签: multithreading rust rwlock


    【解决方案1】:

    问题是:锁定和解锁需要发生在同一个线程上。例如,这是pthread 的限制。

    幸运的是,Rust 类型系统的表达能力足以对此进行建模:通过将 RwLockReadGuard 设为 !Send,它可以防止意外共享锁!祝 Rust 万岁!

    所以你可以在不同的回调函数中锁定和解锁......但在同一个线程上。

    在您的示例中,这就像在线程内移动 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();
            }
        });
    }
    

    【讨论】:

    • 我已经想到了这一点并尝试了您的解决方案。但后来它说handler might not live long enough。但这无论如何都不是一个选择,因为在我的真实项目中,我自己没有设置线程。我只是用回调函数提交一个特征对象,然后一个库启动线程。
    • 有趣的错误,与Mutex问题无关:你不能borrow from a sibling。至于你的问题,我很抱歉,但他们是规则。你不能从一个线程锁定,从另一个线程解锁,并期望事情能正常工作:你必须以另一种方式设计你的程序。
    • 但它是同一个线程,不是吗?这是一个线程一遍又一遍地调用callback()。我希望它在其中几个周期内保持锁定。
    • @JohannesMueller:你确定是吗?如果库使用线程池怎么办?如果当前版本的库不使用线程池但下一个版本使用线程池怎么办(即,小心使用unsafe 绕过限制)?无论如何,您目前遇到的问题是您无法锁定同级字段,也许解决那个问题会引导您转向另一个更适应的设计?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-19
    • 2014-03-01
    • 1970-01-01
    • 2010-09-29
    相关资源
    最近更新 更多