在tokio_cron_scheduler的多个new_repeated_async任务中复用同一值的问题
解决Rust中tokio_cron_scheduler任务捕获变量的所有权冲突问题
问题重现
以下代码尝试使用tokio_cron_scheduler创建重复执行的异步任务,共享同一个Obj实例,但编译时触发错误:cannot move out of obj, a captured variable in an FnMut closure
use std::time::Duration; use tokio_cron_scheduler::{JobScheduler, Job}; #[derive(Clone, Debug)] struct Obj { a: String, b: u64, } #[tokio::main] async fn main() { let obj = Obj { a: "A".to_string(), b: 64, }; let sched = JobScheduler::new().await.unwrap(); let job = Job::new_repeated_async(Duration::from_secs(2), |_, _| Box::pin(async move { foo(&obj); })).unwrap(); sched.add(job).await.unwrap(); let job = Job::new_repeated_async(Duration::from_secs(5), |_, _| Box::pin(async move { bar(&obj); })).unwrap(); sched.add(job).await.unwrap(); sched.start().await.unwrap(); tokio::time::sleep(core::time::Duration::from_secs(100)).await; } fn foo(o: &Obj) { println!("{}", o.a); } fn bar(o: &Obj) { println!("{}", o.b); }
错误原因
Job::new_repeated_async要求传入的闭包实现FnMut trait,因为任务会被重复执行。但代码中async move闭包会尝试捕获obj的所有权,第一次执行任务时,obj会被移出外层闭包,导致后续任务执行时外层闭包无法再访问该变量,触发所有权冲突。
解决方案
使用Arc(原子引用计数)实现线程安全的共享所有权,为每个任务单独克隆Arc实例,确保每个异步任务持有独立的引用副本,避免所有权移动冲突。
修正后的完整代码
use std::sync::Arc; use std::time::Duration; use tokio_cron_scheduler::{JobScheduler, Job}; #[derive(Clone, Debug)] struct Obj { a: String, b: u64, } #[tokio::main] async fn main() { // 用Arc包裹Obj,实现多任务间的线程安全共享 let obj = Arc::new(Obj { a: "A".to_string(), b: 64, }); let sched = JobScheduler::new().await.unwrap(); // 为第一个任务克隆独立的Arc实例 let obj_foo = obj.clone(); let job = Job::new_repeated_async(Duration::from_secs(2), move |_, _| { Box::pin(async move { foo(&obj_foo); }) }).unwrap(); sched.add(job).await.unwrap(); // 为第二个任务克隆独立的Arc实例 let obj_bar = obj.clone(); let job = Job::new_repeated_async(Duration::from_secs(5), move |_, _| { Box::pin(async move { bar(&obj_bar); }) }).unwrap(); sched.add(job).await.unwrap(); sched.start().await.unwrap(); tokio::time::sleep(core::time::Duration::from_secs(100)).await; } fn foo(o: &Arc<Obj>) { println!("{}", o.a); } fn bar(o: &Arc<Obj>) { println!("{}", o.b); }
关键说明
- Arc的作用:
Arc提供线程安全的引用计数,允许多个任务同时持有同一个Obj实例的引用,适配tokio多线程运行时的场景。 - 独立克隆Arc:每个任务必须单独克隆
Arc,确保外层FnMut闭包每次执行时,异步任务都能持有自己的Arc副本,避免所有权被一次性移走。 - 外层闭包使用
move:外层闭包通过move关键字将克隆后的Arc移入闭包,保证异步任务能稳定持有引用,不会出现变量生命周期问题。
内容的提问来源于stack exchange,提问作者Matthew Davis
相关产品推荐
相关产品推荐

