Rust中OnceCell的Lazy在Serenity命令处理器中阻塞问题求解
问题原因分析与解决方法
核心问题是异步运行时死锁,具体原因如下:
当你在
async main中解引用BOT时,main函数由tokio runtime驱动(你应该使用了#[tokio::main]注解)。此时futures::executor::block_on会在main线程中以单线程方式执行异步任务,Http请求的IO操作能正常获取tokio runtime的资源,因此整个初始化流程可以顺利完成,打印出"0,1,2,3"。但在斜杠命令处理器中,代码本身运行在tokio的worker线程里。第一次解引用
BOT触发初始化时,futures::executor::block_on会阻塞当前的tokio worker线程,等待异步任务完成。而Http请求的.await需要tokio runtime分配线程处理IO操作,此时唯一可用的worker线程已被block_on卡死,无法处理IO任务,导致流程卡在第一个.await处,只打印出"0,1"。
简单来说:你在tokio线程中使用了阻塞式的执行器,锁死了当前线程,导致异步任务所需的IO资源无法被调度,最终造成死锁。
解决方法
不要等到命令触发时才初始化BOT,而是在机器人启动阶段(main函数中)提前完成异步初始化,用OnceCell替代Lazy来存储全局实例:
use once_cell::sync::OnceCell; use serenity::{http::Http, model::user::User}; pub struct Bot { pub user: User, pub owner: User, } pub static BOT: OnceCell<Bot> = OnceCell::new(); #[tokio::main] async fn main() { // 提前初始化Bot信息 let http_client = Http::new(env!("DISCORD_BOT_TOKEN")); let user = http_client .get_current_user() .await .expect("获取机器人用户信息失败") .into(); let owner = http_client .get_current_application_info() .await .expect("获取应用信息失败") .owner; // 将初始化好的实例存入全局OnceCell BOT.set(Bot { user, owner }).unwrap(); // 后续启动机器人的逻辑... }
之后在命令处理器中直接通过BOT.get().unwrap()获取实例即可,完全避免阻塞问题。
内容的提问来源于stack exchange,提问作者Fointard
相关产品推荐
相关产品推荐

