简体   繁体   English

在 Rust 中创建两个对结构是线程安全的可变引用

[英]Create two mutable references that are thread safe to a struct in Rust

I'm trying to create async Reader and Writer in tokio, these require Send, and must be thread safe.我正在尝试在 tokio 中创建异步读取器和写入器,这些需要发送,并且必须是线程安全的。 (does not seem to be a way to write single threaded tokio code that avoids mutexts) (似乎不是编写避免 mutexts 的单线程 tokio 代码的方法)

The reader and writer both need to interact, eg read data might result in a response.读取器和写入器都需要交互,例如读取数据可能会导致响应。

I would like both Reader and Writer to have a thread safe pointer to a Session that can ensure communication between the two.我希望 Reader 和 Writer 都有一个指向 Session 的线程安全指针,可以确保两者之间的通信。

/// function on the 
impl Session {

pub fn split(&mut self, sock: TcpStream) -> (Reader, Writer) {
    let (read_half, write_half) = sock.split();

    let session = Arc::new(RwLock::new(self)); // <- expected lifetime

    ( Reader::new(session, read_half), Writer::new(Arc::clone(session), write_half) )
}
...
}

pub struct Reader {
    session: Arc<RwLock<&mut StompSession>>,
    read_half: ReadHalf<TcpStream>,
    ...

pub struct Writer {
    session: Arc<RwLock<&mut StompSession>>,
    write_half: WriteHalf<TcpStream>,
    ....

I dont really understand the difference between Arc<RwLock<&mut StompSession>> and Arc<RwLock<StompSession>> both can only be talking about pointers.我不太明白Arc<RwLock<&mut StompSession>>Arc<RwLock<StompSession>>之间的区别都只能谈论指针。

Naturally come at this by stuggling with the borrow checker and rust book only has examples for RRwLock with integers, not mutable "objects".通过使用借用检查器和 rust 书很自然地遇到了这个问题,只有整数的 RRwLock 示例,而不是可变的“对象”。

Let's start by clearing a few things:让我们从清除一些东西开始:

does not seem to be a way to write single threaded tokio code that avoids mutexes似乎不是编写避免互斥锁的单线程 tokio 代码的方法

The Mutex requirement has nothing to do with single-threadedness but with mutable borrows. Mutex要求与单线程无关,而是与可变借用有关。 Whenever you spawn a future, that future is its own entity;每当你产生一个未来时,那个未来就是它自己的实体; it isn't magically part of your struct and most definitely does not know how to keep a &mut self .它不是你struct的神奇部分,而且绝对不知道如何保持&mut self That is the point of the Mutex - it allows you to dynamically acquire a mutable reference to inner state - and the Arc allows you to have access to the Mutex itself in multiple places.这就是Mutex的重点——它允许您动态获取对内部 state 的可变引用——并且Arc允许您在多个位置访问Mutex本身。

Their non-synchronized equivalents are Rc and Cell / RefCell , by the way, and their content (whether synchronized or unsynchronized) should be an owned type.顺便说一下,它们的非同步等价物是RcCell / RefCell ,它们的内容(无论是同步的还是非同步的)应该是一个拥有的类型。

The Send requirement actually shows up when you use futures on top of tokio , as an Executor requires futures spawned on it to be Send (for obvious reasons - you could make a spawn_local method, but that'd cause more problems than it solves).当您在tokio之上使用futures时,实际上会出现Send要求,因为Executor要求在其上生成的期货是Send (出于显而易见的原因 - 您可以创建spawn_local方法,但这会导致比它解决的问题更多的问题)。

Now, back to your problem.现在,回到你的问题。 I'm going to give you the shortest path to an answer to your problem.我将为您提供解决问题的最短路径 This, however, will not be completely the right way of doing things;然而,这并不是完全正确的做事方式; however, since I do not know what protocol you're going to lay on top of TcpStream or what kind of requirements you have, I cannot point you in the right direction (yet).但是,由于我不知道您将在TcpStream之上放置什么协议或您有什么样的要求,所以我无法为您指明正确的方向(目前)。 Comments are there for that reason - give me more requirements and I'll happily edit this!出于这个原因,有评论 - 给我更多要求,我会很乐意编辑这个!

Anyway.反正。 Back to the problem.回到问题。 Since Mutex<_> is better used with an owned type, we're going to do that right now and "fix" both your Reader and Writer :由于Mutex<_>更适合与拥有的类型一起使用,我们现在将这样做并“修复”您的ReaderWriter

pub struct Reader {
    session: Arc<RwLock<Session>>,
    read_half: ReadHalf<TcpStream>,
}

pub struct Writer {
    session: Arc<RwLock<StompSession>>,
    write_half: WriteHalf<TcpStream>,
}

Since we changed this, we also need to reflect it elsewhere, but to be able to do this we're going to need to consume self .由于我们改变了这一点,我们还需要在其他地方反映它,但为了能够做到这一点,我们将需要消耗self It is fine, however, as we're going to have an Arc<Mutex<_>> copy in either object we return:但是,这很好,因为我们将在 object 中返回Arc<Mutex<_>>副本:

impl Session {

    pub fn split(self, sock: TcpStream) -> (Reader, Writer) {
        let (read_half, write_half) = sock.split();

        let session = Arc::new(RwLock::new(self));
        ( Reader::new(session.clone(), read_half), Writer::new(session, write_half) )
    }
}

And lo and behold, it compiles and each Writer / Reader pair now has its own borrowable (mutably and non-mutably) reference to our session!你瞧,它编译了,每个Writer / Reader对现在都有自己的可借用(可变和非可变)对我们会话的引用!

The playground snippet highlights the changes made. 操场片段突出显示所做的更改。 As I said, it works now, but it's going to bite you in the ass the moment you try to do something as you're going to need something on top of both ReadHalf and WriteHalf to be able to use them properly.正如我所说,它现在可以工作,但是当你尝试做某事时它会咬你一口,因为你需要在ReadHalfWriteHalf之上的东西才能正确使用它们。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM