微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何在异步上下文中访问全局资源?

如何解决如何在异步上下文中访问全局资源?

我一直在用 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 举报,一经查实,本站将立刻删除。