为何Rc可在异步函数中运行?线程安全相关技术疑问
问题解答
你的代码不会引发线程安全问题,原因如下:
Rc的线程安全限制本质
Rc用普通整数实现引用计数,没有原子操作保护,跨线程并发修改(clone或drop)会导致数据竞争,因此Rc默认不实现 Sendtrait——这意味着持有Rc的任务无法被安全地在多个线程间转移。Tokio调度器的适配逻辑
虽然#[tokio::main]默认是多线程运行时,但Tokio会自动识别任务是否实现Send:
- 对于
!Send的任务(比如你的test函数返回的Future,它在await期间持有Rc),Tokio会将其固定在单个线程上执行,不会调度到其他线程。因此Rc的引用计数始终在同一个线程内被修改,不会出现跨线程的数据竞争。
- 代码能编译的原因
Tokio并没有强制要求所有任务必须是Send,它支持运行非Send任务,只是会限制这类任务的执行范围,确保符合安全规则。
额外建议
- 如果你确实需要在多线程间共享引用计数对象,应该用
std::sync::Arc(原子引用计数),它的引用计数操作是线程安全的,实现了Send和Synctrait。 - 代码中混用了Tokio和async_std的sleep,虽然能运行,但建议统一使用Tokio的
tokio::time::sleep,避免运行时兼容性问题。
修改后的线程安全示例(用Arc替代Rc):
use std::sync::Arc; use tokio::join; use tokio::time::{sleep, Duration}; async fn test(t: Arc<String>) { let k = t; println!("1. test,{:?}", k); sleep(Duration::from_secs(1)).await; println!("2. test,{:?}", k); } #[tokio::main] async fn main() { let r = Arc::new("abc".to_string()); let f1 = test(r.clone()); let f2 = test(r.clone()); join!(f1, f2); }
内容的提问来源于stack exchange,提问作者chai
相关产品推荐
相关产品推荐

