如何将lazy_static与async/await初始值设定项一起使用



我使用带有tokio的mongodb作为异步运行时。

我想全局初始化MongoDBClient,所以我使用了惰性静态机箱。问题是客户端异步连接,lazy_static!不支持async关键字:

use mongodb::Client;
async {
let client = Client::with_uri_str(&env_var("MONGO_URL")).await.unwrap();
}

那么如何初始化client呢?

相关:

  • 使用';等待';用lazy_static!铁锈中的宏
  • 异步/等待支持?关于懒惰静态问题跟踪器

如果使用新的运行时,并且惰性静态首先在现有运行时的上下文中使用,如本例所示:

use lazy_static::lazy_static;
use mongodb::Client;
lazy_static! {
static ref CLIENT: Client = {
tokio::runtime::Runtime::new().unwrap().block_on(async {
let uri = std::env::var("MONGO_URL").unwrap();
let client = Client::with_uri_str(&uri).await.unwrap();
client
})
};
}
#[tokio::main]
async fn main() {
let _db = CLIENT.database("local");
}

你会得到提到的错误:

thread 'main' panicked at 'Cannot start a runtime from within a runtime. This happens because a function (like `block_on`) attempted to block the current thread while the thread is being used to drive asynchronous tasks.', C:Userskmdreko.cargoregistrysrcgithub.com-1ecc6299db9ec823tokio-1.6.1srcruntimeenter.rs:39:9

您可以通过使用不同的运行时(futurestokioasync-std(来规避此问题,但这并不理想,因为它仍然会阻塞底层运行时。


解决这个问题的一个相对直接的方法是而不是尝试延迟执行并立即在main中初始化它。这样,您就可以直接使用异步运行时,而不必担心在其他地方需要:

use mongodb::Client;
use once_cell::sync::OnceCell;
static CLIENT: OnceCell<Client> = OnceCell::new();
#[tokio::main]
async fn main() {
let uri = std::env::var("MONGO_URL").unwrap();
let client = Client::with_uri_str(&uri).await.unwrap();
CLIENT.set(client).unwrap();
let _db = CLIENT.get().unwrap().database("local");
}

如有必要,这可以通过OnceCell(如上所述(或类似RwLock的东西来完成。


对于您试图实现的目标,最直接的答案是使用异步机箱,这使得它使用接收器的运行时来驱动异步功能。

use async_once::AsyncOnce;
use lazy_static::lazy_static;
use mongodb::Client;
lazy_static! {
static ref CLIENT: AsyncOnce<Client> = AsyncOnce::new(async {
let uri = std::env::var("MONGO_URL").unwrap();
let client = Client::with_uri_str(&uri).await.unwrap();
client
});
}
#[tokio::main]
async fn main() {
let _db = CLIENT.get().await.database("local");
}

这假设客户端的所有或几乎所有使用都将在异步上下文中进行。

最新更新