我设置超时为1s,但是任务执行到3s,却没有发生panic。
#代码
#[should_panic]
fn test_timeout() {
let rt = create_runtime();
let timeout_duration = StdDuration::from_secs(1);
let sleep_duration = StdDuration::from_secs(3);
let _guard = rt.enter();
let timeout = time::timeout(timeout_duration, async {
log("timeout running");
thread::sleep(sleep_duration);
log("timeout finsihed");
"Ding!".to_string()
});
rt.block_on(timeout).unwrap();
}
Run Code Online (Sandbox Code Playgroud)
thread::sleep在异步代码中使用几乎总是错误的。
从概念上讲,超时的工作原理如下:
tokio产生一个计时器,该计时器将在指定的持续时间后唤醒。tokio孕育你的未来。如果它返回Poll::Ready,则计时器被丢弃,未来成功。如果它返回Poll::Pending,tokio则等待下一个事件,即唤醒您的未来或计时器。tokio再次轮询它。如果它再次返回Poll::Ready,计时器将被丢弃,未来成功。tokio则最后一次轮询 future;如果仍然是Poll::Pending,则超时并且不再轮询,并timeout返回错误。然而,就您而言, future 不会返回-Poll::Pending它会阻塞在thread::sleep. 因此,即使计时器可以在一秒过去后触发,tokio也无法做出反应 - 它等待 future 返回,future 仅在线程解除阻塞后才返回,并且,由于await块内没有任何内容,因此它返回Poll::Ready- 所以计时器甚至没有被检查。
要解决此问题,您应该使用tokio::time::sleep异步代码内的任何暂停。有了它,未来就可以正确地超时。为了说明这一说法,让我们看一下与原始代码等效的独立示例:
use core::time::Duration;
use tokio::time::timeout;
#[tokio::main]
async fn main() {
let timeout_duration = Duration::from_secs(1);
let sleep_duration = Duration::from_secs(3);
timeout(timeout_duration, async {
println!("timeout running");
std::thread::sleep(sleep_duration);
println!("timeout finsihed");
"Ding!".to_string()
})
.await
.unwrap_err();
}
Run Code Online (Sandbox Code Playgroud)
正如您已经注意到的,这会失败 -unwrap_err调用时会出现恐慌Ok,并且Ok由于 future 没有正确超时而返回超时。
但是当替换std::thread::sleep(...)为tokio::time::sleep(...).await...
use core::time::Duration;
use tokio::time::timeout;
#[tokio::main]
async fn main() {
let timeout_duration = Duration::from_secs(1);
let sleep_duration = Duration::from_secs(3);
timeout(timeout_duration, async {
println!("timeout running");
tokio::time::sleep(sleep_duration).await;
println!("timeout finsihed");
"Ding!".to_string()
})
.await
.unwrap_err();
}
Run Code Online (Sandbox Code Playgroud)
...我们得到了预期的行为 - Playground。
| 归档时间: |
|
| 查看次数: |
709 次 |
| 最近记录: |