【问题标题】:Is there a way to run a closure on RwLock drop?有没有办法在 RwLock drop 上运行关闭?
【发布时间】:2019-11-08 00:33:42
【问题描述】:

我有一个将可变状态隐藏在 RwLock 后面的程序。我想做的是,当它被可变借用(RW_LOCK.write())时,它应该做一些事情(即尝试写入文件,清理 rwlock 后面的数据等)

例如:

let DATA: RwLock<Data> = RwLock::new(Data { content: Default::default() } );

fn do_something() {
    let mut state = DATA.write().unwrap();

    state.change(5);
    // ...
    // Here, just before `state` goes out of scope (where it gets dropped and `RwLock` will allow 
    // other threads read/write access to `Data`, I would like for `RwLock` to auto-run `state.cleanup()`. 
}

有没有办法做到这一点,还是我必须重新实现RwLock

【问题讨论】:

  • 您的问题标题提到运行闭包,但正文引用运行 state.cleanup()。我认为标题需要更新?如果您可以使您的示例足够完整以进行编译,那也很棒。 Lazy 的用法似乎也与问题无关,因为可以在此示例的上下文中直接在函数中声明锁。
  • @loganfsmyth 我看不到在没有闭包的情况下调用 state.cleanup 的任何方式,但主要的一点是,当写锁被删除时,我应该能够以某种方式对 Data 做一些事情跨度>
  • 你在哪里看到影响这个的关闭?我想我理解你想要什么,但我不认为我会遵循闭包的位置,所以我希望为你澄清这一点。

标签: rust raii


【解决方案1】:

您可以使用包装器类型来做到这一点:

Playground

use std::ops::{Deref, DerefMut, Drop};
use std::sync::{RwLock, RwLockWriteGuard};

type CleanupClosure<'a> = Fn(&mut RwLockWriteGuard<'a, Data>);

struct Data {
    content: String,
}

impl Data {
    fn change(&mut self, num: i32) {
        println!("Changed to {}", num);
        self.content = num.to_string();
    }
}

struct RwLockWriteWrapper<'a, F: CleanupClosure<'a>>(RwLockWriteGuard<'a, Data>, F);

impl<'a, F: CleanupClosure<'a>> Deref for RwLockWriteWrapper<'a, F> {
    type Target = RwLockWriteGuard<'a, Data>;

    fn deref(&self) -> &RwLockWriteGuard<'a, Data> {
        &self.0
    }
}

impl<'a, F: CleanupClosure<'a>> DerefMut for RwLockWriteWrapper<'a, F> {
    fn deref_mut(&mut self) -> &mut RwLockWriteGuard<'a, Data> {
        &mut self.0
    }
}

impl<'a, F: CleanupClosure<'a>> Drop for RwLockWriteWrapper<'a, F> {
    fn drop(&mut self) {
        println!("Cleaning up!");
        self.1(&mut self.0)
    }
}

fn main() {
    let data: RwLock<Data> = RwLock::new(Data {
        content: "Start".to_owned(),
    });
    do_something(&data);
    do_something(&data);
}

fn do_something(data: &RwLock<Data>) {
    // Write your own cleanup logic here
    let mut state = RwLockWriteWrapper(data.write().unwrap(), |state| {
        state.content = "Cleaned up".to_owned()
    });

    println!("do_something start: {}", state.content);
    state.change(5);

    println!("do_something after change: {}", state.content);
} // Automatically run cleanup here

它确实要求您在调用 .write() 时记住包装类型。您可以将 RwLock 本身包装在另一种类型中,该类型也会返回 RwLockWriteWrapper 以实现自动化。

这确实变得很冗长,所以我找到了a crate that impls the deref trait for you

我仍然不确定你在标题中提到的闭包是什么意思。

【讨论】:

  • 您的 cleanup 方法是硬编码的。 OP 希望提供一个闭包,因此一个锁可以做与另一个不同的事情。对于您的示例,RwLockWriteWrapper 将采用两个参数。
【解决方案2】:

你可以创建一个实现Drop的包装器:

struct CleanOnDrop<D, F>
where
    F: FnOnce(&mut D),
{
    data: D,
    cleanup: Option<F>,
}

impl<D, F> CleanOnDrop<D, F>
where
    F: FnOnce(&mut D),
{
    pub fn new(data: D, cleanup: F) -> Self {
        Self { data, cleanup: Some(cleanup) }
    }
}

impl<D, F> Drop for CleanOnDrop<D, F>
where
    F: FnOnce(&mut D)
{
    fn drop(&mut self) {
        if let Some(mut cleanup) = self.cleanup.take() {
            cleanup(&mut self.data);
        }
    }
}

为了方便,你可能还想实现DerefDerefMut,这样你就可以直接调用它的方法了:

use std::ops::{Deref, DerefMut};

impl<D, F> Deref for CleanOnDrop<D, F>
where
    F: FnOnce(&mut D),
{
    type Target = D;
    fn deref(&self) -> &D {
        &self.data
    }
}

impl<D, F> DerefMut for CleanOnDrop<D, F>
where
    F: FnOnce(&mut D),
{
    fn deref_mut(&mut self) -> &mut D {
        &mut self.data
    }
}

像这样使用包装器:

let data = RwLock::new(CleanOnDrop::new(
    Data {
        content: Default::default(),
    },
    |state| {
        state.cleanup();
    },
));

【讨论】:

    猜你喜欢
    • 2011-09-18
    • 1970-01-01
    • 1970-01-01
    • 2017-02-02
    • 2014-12-01
    相关资源
    最近更新 更多