编译器说即使将数据包装在Mutex中,也无法在线程之间安全地共享数据 [英] Compiler says that data cannot be shared between threads safely even though the data is wrapped within a Mutex

查看:188
本文介绍了编译器说即使将数据包装在Mutex中,也无法在线程之间安全地共享数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Rocket,它具有一个State,它将传递给HTTP请求.该结构包含一个Mutex<DatastoreInstance>,它可以访问SQLite数据库,并用互斥锁锁定,以确保读取和写入的安全.

I'm using Rocket which has a State that it passes to the HTTP requests. This struct contains a Mutex<DatastoreInstance> which gives access to a SQLite database and is locked with a mutex to make read and writes safe.

pub struct DatastoreInstance {
    conn: Connection,
}

DatastoreInstance结构看起来像这样,只有一个SQLite连接时,一切正常,但是我还想在该结构中添加一个事务对象:

When the DatastoreInstance struct looked like this, with only a SQLite connection everything worked fine, but I then also wanted to add a transaction object within this struct:

pub struct DatastoreInstance {
    conn: Connection,
    events_transaction: Transaction,
}

这没有编译,因为Transaction对象需要引用一个Connection对象,该对象应该具有它知道的生存期. 我正在使用的rusqlite中的ConnectionTransaction对象定义如下:

This did not compile because the Transaction object needs to reference a Connection object which should have a lifetime which it is aware of. The Connection and Transaction objects within rusqlite which I am using are defined as following:

pub struct Connection {
    db: RefCell<InnerConnection>,
    cache: StatementCache,
    path: Option<PathBuf>,
}

pub struct Transaction<'conn> {
    conn: &'conn Connection,
    drop_behavior: DropBehavior,
}

要解决生命周期问题,我必须添加以下生命周期参数以使其正常工作:

To solve the lifetime issues I had to add these lifetime parameters to get it working:

pub struct DatastoreInstance<'a> {
    conn: Connection,
    events_transaction: Transaction<'a>,
}

这是结果,并且应该根据我对生存期和互斥量的理解而起作用,但是现在我收到一个编译器错误,告诉我:

This was the result and was supposed to work according to my understanding of both lifetimes and mutexes, but I now get a compiler error telling me:

`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中,这应保证每次只有一个线程引用此对象.

According to my understanding of mutexes, this code should be valid because the whole DatastoreInstance struct is wrapped within a Mutex which should guarantee that only one thread is referencing this object at a time.

我想念什么?

为什么编译器位于Transaction中引用的Connection中而不是仅仅位于Connection中之后,为什么不再认为RefCell是安全的?

Why doesn't the compiler find RefCell to be safe anymore after being within a Connection referenced within a Transaction instead of solely within a Connection?

我对互斥锁的工作方式了解不深吗?我的一生是否无效,并且以某种方式破坏了读/写安全性?在同一结构中具有ConnectionTransaction的设计是否会破坏读/写安全性?我是否需要以某种方式重新设计我的数据结构以使其安全?还是我只是想念一些很明显的东西?

Do I have a bad understanding of how mutexes work? Are my lifetimes invalid and somehow break read/write safety? Is the design of having the Connection and Transaction within the same struct a bad design which breaks read/write safety? Do I need to redesign my data structures somehow to make this safe? Or am I just missing something very obvious?

推荐答案

Mutex仅为SendSync

A Mutex is only Send or Sync if the value it contains is itself Send:

impl<T: ?Sized + Send> Send for Mutex<T>    
impl<T: ?Sized + Send> Sync for Mutex<T>

&T仅是Send T时是Sync :

A &T is only Send when T is Sync:

impl<'a, T> Send for &'a T
where
    T: Sync + ?Sized, 

并且RefCell 永远不会

impl<T> !Sync for RefCell<T>
where
    T: ?Sized, 

错误消息指出,您的事务包含对RefCell的引用.互斥量无关紧要,跨线程共享它本质上不是内存安全的.一个简单的复制品:

As the error message states, your transaction contains a reference to a RefCell. It doesn't matter that there's a mutex, it's inherently not memory-safe to share it across threads. A simple reproduction:

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) {}
   | ^^^^^^^^^^^^^^^^^^^^^^^^^

为什么编译器位于Transaction中引用的Connection中而不是仅仅位于Connection中之后,为什么不再认为RefCell是安全的?

Why doesn't the compiler find RefCell to be safe anymore after being within a Connection referenced within a Transaction instead of solely within a Connection?

RefCell很好,它是对RefCell引用.

在同一结构中包含ConnectionTransaction的设计是错误的设计[...]我需要重新设计我的数据结构

Is the design of having the Connection and Transaction within the same struct a bad design [...] Do I need to redesign my data structures

是的

  • How to store rusqlite Connection and Statement objects in the same struct in Rust?
  • Why can't I store a value and a reference to that value in the same struct?

这篇关于编译器说即使将数据包装在Mutex中,也无法在线程之间安全地共享数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆