rust - 编译器说数据不能在线程之间安全地共享,即使数据包装在互斥锁中
问题描述
我正在使用 Rocket State
,它传递给 HTTP 请求。这个结构包含一个Mutex<DatastoreInstance>
可以访问 SQLite 数据库并被互斥锁锁定以确保读写安全的结构。
pub struct DatastoreInstance {
conn: Connection,
}
当DatastoreInstance
结构看起来像这样时,只有一个 SQLite 连接一切正常,但我还想在这个结构中添加一个事务对象:
pub struct DatastoreInstance {
conn: Connection,
events_transaction: Transaction,
}
这没有编译,因为Transaction
对象需要引用一个Connection
应该具有它知道的生命周期的对象。我正在使用的 rusqlite 中的Connection
and对象定义如下:Transaction
pub struct Connection {
db: RefCell<InnerConnection>,
cache: StatementCache,
path: Option<PathBuf>,
}
pub struct Transaction<'conn> {
conn: &'conn Connection,
drop_behavior: DropBehavior,
}
为了解决生命周期问题,我必须添加这些生命周期参数才能使其正常工作:
pub struct DatastoreInstance<'a> {
conn: Connection,
events_transaction: Transaction<'a>,
}
这是结果,应该根据我对生命周期和互斥锁的理解工作,但现在我得到一个编译器错误告诉我:
`std::cell::RefCell<lru_cache::LruCache<std::string::String, rusqlite::raw_statement::RawStatement>>` cannot be shared between threads safely
|
= help: within `rusqlite::Connection`, the trait `std::marker::Sync` is not implemented for `std::cell::RefCell<lru_cache::LruCache<std::string::String, rusqlite::raw_statement::RawStatement>>`
= note: required because it appears within the type `rusqlite::cache::StatementCache`
= note: required because it appears within the type `rusqlite::Connection`
= note: required because of the requirements on the impl of `std::marker::Send` for `&rusqlite::Connection`
= note: required because it appears within the type `datastore::DatastoreInstance<'_>`
= note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Mutex<datastore::DatastoreInstance<'_>>`
= note: required because it appears within the type `endpoints::ServerState<'_>`
= note: required by `rocket::State`
根据我对互斥体的理解,这段代码应该是有效的,因为整个DatastoreInstance
结构都被包裹在一个Mutex
应该保证一次只有一个线程引用这个对象的情况下。
我错过了什么?
为什么编译器在 a 内引用而不是仅在 a内引用RefCell
后发现不再安全?Connection
Transaction
Connection
我对互斥锁的工作原理有不好的理解吗?我的生命周期是否无效并以某种方式破坏了读/写安全?将Connection
andTransaction
放在同一个结构中的设计是否会破坏读/写安全性?我是否需要以某种方式重新设计我的数据结构以确保安全?还是我只是错过了一些非常明显的东西?
解决方案
A Mutex
is onlySend
或者Sync
如果它包含的值是它本身Send
:
impl<T: ?Sized + Send> Send for Mutex<T>
impl<T: ?Sized + Send> Sync for Mutex<T>
A&T
仅Send
当T
是Sync
时:
impl<'a, T> Send for &'a T
where
T: Sync + ?Sized,
而 aRefCell
永远不会Sync
impl<T> !Sync for RefCell<T>
where
T: ?Sized,
如错误消息所述,您的交易包含对 a 的引用RefCell
。有一个互斥锁并不重要,跨线程共享它本质上不是内存安全的。一个简单的再现:
use std::{cell::RefCell, sync::Mutex};
struct Connection(RefCell<i32>);
struct Transaction<'a>(&'a Connection);
fn is_send<T: Send>(_: T) {}
fn main() {
let c = Connection(RefCell::new(42));
let t = Transaction(&c);
let m = Mutex::new(t);
is_send(m);
}
error[E0277]: `std::cell::RefCell<i32>` cannot be shared between threads safely
--> src/main.rs:13:5
|
13 | is_send(m);
| ^^^^^^^ `std::cell::RefCell<i32>` cannot be shared between threads safely
|
= help: within `Connection`, the trait `std::marker::Sync` is not implemented for `std::cell::RefCell<i32>`
= note: required because it appears within the type `Connection`
= note: required because of the requirements on the impl of `std::marker::Send` for `&Connection`
= note: required because it appears within the type `Transaction<'_>`
= note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Mutex<Transaction<'_>>`
note: required by `is_send`
--> src/main.rs:6:1
|
6 | fn is_send<T: Send>(_: T) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^
为什么编译器在 a 内引用而不是仅在 a内引用
RefCell
后发现不再安全?Connection
Transaction
Connection
RefCell
很好,它是对 a的引用RefCell
,那不是。
将
Connection
andTransaction
包含在同一个结构中的设计是一个糟糕的设计 [...] 我需要重新设计我的数据结构吗
是的。
推荐阅读
- c++ - 无法在 R (Mac) 上安装 TDA
- python - 如何将数据帧的行迭代成多行句子?
- node.js - 令人难以置信的奇怪木偶行为
- javascript - 单元测试要与 redux-form 一起使用的自定义 Input React 组件失败
- objective-c - 在“id”类型的对象上找不到读取字典元素的预期方法
' - assembly - NASM,如何直接写入硬盘并在实模式下读取
- clojure - 为什么 clojure.set/union 函数接受其他类型的数据作为输入?
- ms-access - Access 中缺少自动编号
- sql - 使用 rownum 的 SQL Server 同行连接不返回任何值
- python - 如何在列表中的列表中获取两个值?