问题描述
我一直在用 tide
风格的 async-std
框架编写一个小型网络服务。该项目使用的数据库很少使用,但贯穿整个项目。
我认为将它声明为全局变量是合理的,这样我就不必在几乎每个函数接口中都包含它,只是为了将它传递给未使用的。在与编译器争论我违反了多少 Rust 设计原则之后,我决定了以下声明:
pub static DATABASE: RwLock<Database> = const_rwlock(Database::new());
const_rwlock()
是来自 Rwlock
板条箱的常量 parking_lot
构造函数(据我所知,与每晚的 RwLock
相同)和 Database
是我自己的类型,稍后会填充连接详细信息,并以异步方式处理数据库的实际通信和初始化。
一切看起来都很好,因为我可以像这样使用它:
let data = DATABASE.read().get_data(&key).await;
当我尝试将此功能传递到 Web 框架时,问题就出现了。我应该通过传入一个必须是 Send
的异步块闭包来做到这一点,但是当我写这样的东西时:
// the actual function to start the servers looks essentially like this
start_server(|| async move {
// this is run asynchronously for every connection by the framework
let token = do_stuff();
let result = DATABASE.read().check(token).await;
});
我收到一个错误,因为整个未来不再是 Send
。这对我来说很有意义,因为我正在使用 .read()
调用锁定互斥锁,然后将整个内容打包到未来并等待它。这意味着锁定的互斥锁可能会被发送到另一个线程,这似乎不太明智。
这让我觉得我把整个全局变量设计错了,我没有更好的想法。
表示在异步代码中多处使用的资源的好方法是什么?我想避免“到处传递”路线。
这是一个很小的代码示例。我从 tide
crate 中提取了相关代码,以尝试缩小此问题的范围。
use async_std::sync::Arc;
use parking_lot::{const_rwlock,RwLock};
use std::future::Future;
pub static DATABASE: RwLock<Database> = const_rwlock(Database::new());
// const mutex can only be constructed with new() on nightly
//pub static DATABASE: RwLock<Database> = RwLock::new(Database::new());
pub struct Database {
// Here would be the actual connection stored
pool: Option<()>,}
impl Database {
const fn new() -> Self {
Database {
pool: None,}
}
pub async fn init(&mut self) {
self.pool = Some(());
}
pub async fn check(&self,token: &str) -> bool {
if token == "vibe" {
return true;
} else {
return false;
}
}
}
#[async_std::main]
async fn main() {
DATABASE.read().init().await;
Server::new(|| async move {
// XXX There's the problem
let result = DATABASE.read().check("vibe").await;
if result {
Ok(())
} else {
Err(())
}
});
}
// Everything below is part of some library and can't be changed by me
struct Server<H> {
handler: Arc<H>
}
impl<H,Fut> Server<H>
where
// Those requirements are verbatim copied from the framework code
H: Fn() -> Fut + Sync + Send + 'static,Fut: Future<Output = Result<(),()>> + Send + 'static,{
pub fn new(handler: H) -> Self {
Self {
handler: Arc::new(handler),}
}
}
Cargo.toml:
[package]
name = "server"
version = "0.1.0"
edition = "2018"
[dependencies]
parking_lot = "0.11"
async-std = { version = "1.6",features = ["attributes"] }
在其上运行 cargo check
后,我收到描述的错误:
Checking server v0.1.0 (/XXX/server)
error: future cannot be sent between threads safely
--> src/main.rs:36:5
|
36 | Server::new(|| async move {
| ^^^^^^^^^^^ future created by async block is not `Send`
...
60 | pub fn new(handler: H) -> Self {
| ------------------------------ required by `Server::<H>::new`
|
= help: within `impl Future`,the trait `std::marker::Send` is not implemented for `*mut ()`
note: future is not `Send` as this value is used across an await
--> src/main.rs:38:22
|
38 | let result = DATABASE.read().check("vibe").await;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ first,await occurs here,with `DATABASE.read()` maybe used later...
note: `DATABASE.read()` is later dropped here
--> src/main.rs:38:57
|
38 | let result = DATABASE.read().check("vibe").await;
| --------------- ^
| |
| has type `parking_lot::lock_api::RwLockReadGuard<'_,parking_lot::RawRwLock,Database>` which is not `Send`
help: consider moving this into a `let` binding to create a shorter lived borrow
--> src/main.rs:38:22
|
38 | let result = DATABASE.read().check("vibe").await;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
我相信我在高级别正确解释了此错误(无法在线程之间移动锁防护),并且在此错误消息中在低级别(*mut ()
不是 Send
)中进行了解释。
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)