Rust定时器未在指定超时后触发的问题排查
Rust定时器回调未执行的问题排查与解决
我在使用Rust的timer库(版本0.2.0)时遇到问题,定时器回调始终未执行,即使调整超时时间(包括设为0)也无效。测试代码如下:
use std::sync::{Arc, Mutex}; use timer::Timer; struct Counter { value: i32, } impl Counter { fn new() -> Self { Self { value: 0 } } fn increment(&mut self) { self.value += 1; } fn get_value(&self) -> i32 { self.value } } #[test] fn test_1() { let counter = Arc::new(Mutex::new(Counter::new())); let counter_clone_1 = Arc::clone(&counter); let counter_clone_2 = Arc::clone(&counter_clone_1); // Arrange let callback = { move || { println!("Callback executed"); counter_clone_1.lock().unwrap().increment(); } }; let timer = Timer::new(); println!("Timer created"); timer.schedule_with_delay(chrono::Duration::seconds(2), callback); // Assert initial value println!("Initial assert"); assert_eq!(counter_clone_2.lock().unwrap().get_value(), 0); // Act println!("Sleeping for 4 seconds"); std::thread::sleep(std::time::Duration::new(4, 0)); // Assert incremented value println!("Final assert"); assert_eq!(counter.lock().unwrap().get_value(), 1); }
运行结果显示回调未触发,最终断言失败:
Timer created Initial assert Sleeping for 4 seconds Final assert thread 'missed_ping_timer_tests::test_1' panicked at tests\missed_ping_timer_tests.rs:51:5: assertion `left == right` failed left: 0 right: 1
我的Cargo.toml配置:
[dependencies] timer = "0.2.0" chrono = "0.4.39"
问题根源:chrono版本不兼容
timer 0.2.0依赖的是chrono 0.2.x版本,但你在Cargo.toml中指定了chrono 0.4.39。这两个版本的chrono::Duration是完全不同的类型(即使结构相似),导致你传递给schedule_with_delay的超时时间被错误解析,实际延迟远超过预期,因此回调在测试的4秒内不会执行。
另外,即使版本兼容,timer 0.2.x的设计也需要注意:Timer实例必须保持存活直到回调执行完毕,否则实例被drop时会立即终止内部定时器线程,取消所有未执行的任务。
解决方案
方案1:对齐chrono版本
将chrono版本降级到与timer 0.2.0兼容的0.2.x系列,修改Cargo.toml:
[dependencies] timer = "0.2.0" chrono = "0.2.33"
方案2:升级timer到兼容chrono 0.4的版本
timer 0.3.x及以上版本支持chrono 0.4,修改Cargo.toml:
[dependencies] timer = "0.3.0" chrono = "0.4.39"
此版本API与0.2.x基本兼容,你的测试代码无需大幅修改即可正常运行。
方案3:替换为原生线程实现(无第三方依赖)
如果不想依赖timer库,可以直接用标准库线程实现定时器逻辑,避免版本兼容问题:
use std::sync::{Arc, Mutex}; struct Counter { value: i32, } impl Counter { fn new() -> Self { Self { value: 0 } } fn increment(&mut self) { self.value += 1; } fn get_value(&self) -> i32 { self.value } } #[test] fn test_1() { let counter = Arc::new(Mutex::new(Counter::new())); let counter_clone_1 = Arc::clone(&counter); let counter_clone_2 = Arc::clone(&counter_clone_1); // 用标准库线程实现定时器 std::thread::spawn(move || { std::thread::sleep(std::time::Duration::seconds(2)); println!("Callback executed"); counter_clone_1.lock().unwrap().increment(); }); // Assert initial value println!("Initial assert"); assert_eq!(counter_clone_2.lock().unwrap().get_value(), 0); // Act println!("Sleeping for 4 seconds"); std::thread::sleep(std::time::Duration::new(4, 0)); // Assert incremented value println!("Final assert"); assert_eq!(counter.lock().unwrap().get_value(), 1); }
验证说明
选择方案1或2后,你的原代码中的回调会在2秒后正常执行,最终断言会通过。方案3的原生实现更直接,无需依赖第三方库,适合简单的定时场景。
内容的提问来源于stack exchange,提问作者Umberto Gotti
相关产品推荐
相关产品推荐

