如何在循环中使用结构的可变引用来生成异步任务,而不必使用复制/克隆特征?

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

我目前正在构建一个简单的内存数据库以重新“学习”rust,但对于我的生活,我似乎无法找到解决我正在尝试做的事情的方法。

我正在使用 tokio 进行异步 io/网络。

代码如下:

let mut cache = Cache {
    buffer: vec![0; allocated],
    mapping: HashMap::new()
};

let listener = TcpListener::bind("0.0.0.0:9055").await?;

loop {
    let (mut socket, _) = listener.accept().await?;

    tokio::spawn(async move {
        process_client(&mut cache, &mut socket).await?;
        Ok::<_, io::Error>(())
    });
}

缓存结构的定义:

struct Cache {
    buffer: Vec<u8>,
    mapping: HashMap<String, (usize, usize)>
}

现在我不断收到的错误是:

error[E0382]: use of moved value: `cache`
   --> src\main.rs:172:22
    |
162 |       let mut cache = Cache {
    |           --------- move occurs because `cache` has type `Cache`, which does not implement the `Copy` trait
...
172 |           tokio::spawn(async move {
    |  ______________________^
173 | |             process_client(&mut cache, &mut socket).await?;
    | |                                 ----- use occurs due to use in generator
174 | |             Ok::<_, io::Error>(())
175 | |         });
    | |_________^ value moved here, in previous iteration of loop

如何在这个异步任务中将

cache
的可变引用传递给
process_client()
方法,而不必克隆/复制整个
cache
结构? (因为如果缓冲区很大,克隆/复制将对性能造成巨大影响)

我尝试过实现生命周期,但我对生命周期以及如何正确使用它们没有很好的理解。

asynchronous rust memory rust-tokio
1个回答
0
投票

如果没有某种形式的同步,这是无法完成的。这是设计使然,编译器会阻止您出现潜在的竞争条件。考虑 tokio 在多个线程上运行的情况,spawn 被命中并延迟到另一个线程,然后循环重复并且第二个 spawn 被命中并延迟到另一个线程。您现在在两个不同的线程上有两个对

cache
的可变引用,并且可能存在并发访问。

要解决这个问题,您可以使用

Mutex
之类的东西,在这种情况下,您应该使用
tokio::sync::Mutex
(如果您只需要不可变借用,则可以使用
RwLock
),以及
Arc
(否则无法确保
cache
将存在足够长的时间并允许对其进行多次有效引用)。例如:

let mut cache = Arc::new(Mutex::new(Cache {
    buffer: vec![0; allocated],
    mapping: HashMap::new()
}));
// ...
tokio::spawn({
    let cache = cache.clone();
    async move {
        process_client(&mut cache.lock().await, &mut socket).await?;
        // ...
    }
});
© www.soinside.com 2019 - 2024. All rights reserved.