我目前正在构建一个简单的内存数据库以重新“学习”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
结构? (因为如果缓冲区很大,克隆/复制将对性能造成巨大影响)
我尝试过实现生命周期,但我对生命周期以及如何正确使用它们没有很好的理解。
如果没有某种形式的同步,这是无法完成的。这是设计使然,编译器会阻止您出现潜在的竞争条件。考虑 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?;
// ...
}
});