Actix框架中send触发处理但do_send未被处理的原因
Actix
do_send 消息未触发处理的认知偏差排查 本次核心诉求为厘清自身对Actix认知模型的疏漏,在此先行致谢。
基础示例代码
消息与响应定义
#[derive(Message)] #[rtype(result = "Responses")] enum Messages {Ping,Pong} #[derive(Debug)] enum Responses {GotPing,GotPong} impl<A, M> MessageResponse<A, M> for Responses where A: Actor, M: Message<Result = Responses> { fn handle(self, _ctx: &mut A::Context, tx: Option<OneshotSender<M::Result>>) { if let Some(tx) = tx {tx.send(self);} } }
main函数实现
#[actix_rt::main] pub async fn main() { let addr = ActorQuox.start(); let pongfut_async = addr.send(Messages::Pong).await; pongfut_async.map_or(Responses::GotPong, |res| { println!("Got result: {:?}", res); res }); let ping = addr.do_send(Messages::Ping); }
预期与实际输出不符
预期输出
Got message Got result: GotPong Got message
实际输出
Got message Got result: GotPong
核心认知偏差与成因
此前认知:impl Handler<Messages> for ActorQuox的处理逻辑会对do_send、send、try_send三个方法均触发执行,三者在Addr的实现中都执行了消息投递逻辑,仅返回值签名存在差异。
该认知的偏差点如下:
- 三个发送方法确实都会将消息投递到Actor邮箱,Handler也确实会对所有投递成功的消息触发执行,但前提是Actix运行时没有提前终止,给Actor留出处理邮箱消息的时间。
- 三个方法的等待逻辑差异是问题核心:
send是异步方法,会返回一个Future,await该Future时会暂停当前任务,直到目标Actor处理完对应消息、通过oneshot通道返回响应后才会继续往下执行。示例中addr.send(Messages::Pong).await会等待Pong消息被Actor完整处理(包括打印"Got message"、返回响应),后续的结果打印逻辑也会正常执行。do_send是发后即忘的同步方法,仅负责将消息塞入Actor的邮箱队列就立刻返回,完全不会等待消息被Actor处理。示例中调用完addr.do_send(Messages::Ping)后,main函数直接走到末尾结束,actix_rt运行时会随main函数退出直接关停,此时Ping消息还躺在Actor的邮箱队列里没被取出处理,自然不会输出第三条"Got message"。try_send逻辑和do_send基本一致,唯一区别是当Actor邮箱已满时会直接返回错误,不会阻塞等待入队,同样不会等待消息处理完成。
验证方式
在do_send调用后增加一个短延时,给Actor留出处理消息的时间,就能看到预期的第三条输出:
let ping = addr.do_send(Messages::Ping); // 增加100ms延时等待运行时处理邮箱内的剩余消息 tokio::time::sleep(std::time::Duration::from_millis(100)).await;
或者将do_send替换为send并await,也能稳定看到Ping消息的处理输出。
内容的提问来源于stack exchange,提问作者rtviii
相关产品推荐
相关产品推荐

