Rust:在使用粗粒度锁访问的数据结构中使用 RefCell 安全吗?

问题描述 投票:0回答:1

我正在编写一些 tokio 异步代码。我有一个多索引数据结构,将用户保留在我想要使用粗粒度锁(与每个对象锁相反)保护他们的位置。我有这样的东西:

use tokio::sync::RwLock;

struct User {
  id: u64,
  name: String,
}

// This class is not thread-safe.
struct UserDb {
  by_id: HashMap<u64, Arc<RefCell<User>>>,
  by_name: HashMap<String, Arc<RefCell<User>>>,
}

impl UserDb {
  pub fn add_user(&mut self, name: String) -> Result<(), Error> {
    // ...
  }
}

// This class is thread-safe.
struct AsyncDb {
  users: RwLock<UserDb>,
}

impl AsyncDb {
  pub async fn add_user(&self, name: String) -> Result<(), Error> {
    self.users.write().await.add_user(name)
  }
}

// QUESTION: Are these safe?
unsafe impl Send for AsyncDb {}
unsafe impl Sync for AsyncDb {}

如果末尾没有

Send
Sync
特征,编译器会抱怨
RefCell<User>
不是
Send
Sync
(合理地如此),因此通过
AsyncDb::add_user
访问/修改不安全。

我的解决方案是为数据结构实现

Send
Sync
,因为
AsyncDb
UserDb
周围有一个粗粒度锁,其中包含所述
RefCell

这是正确的解决方案吗?它是否违反了任何不变量?有没有更好的方法来处理这个问题?

注意:这里是 Rust 初学者。我可能有很多概念上的差距,所以如果事情没有意义,请指出。

asynchronous rust concurrency traits rust-tokio
1个回答
0
投票

首先,我不确定为什么需要手动实现

Send
。当
RefCell<T>
也实现时,
RwLock<T>
Send
都实现
T
,因此
AsyncDb
应自动实现
Send

为了解决您问题的实质,这几乎肯定是听起来,除非您即使在从用户读取数据时也采用写锁。

RefCell::borrow*()
函数不是线程安全的,因为它们不以原子方式维护内部引用计数。这意味着仅由读锁保护时使用
borrow()
来读取
RefCell<User>
是不合理的。

如果您已经购买了此特定设计,我强烈建议您将

RwLock
替换为
Mutex

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.