rust - 为什么任务超时时不 panic ?

标签 rust rust-tokio

我设置超时为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();
    }

最佳答案

使用thread::sleep在异步代码中is almost always wrong .

从概念上讲,超时的工作原理如下:

  • tokio生成一个计时器,该计时器将在指定的持续时间后唤醒。
  • tokio孕育你的 future 。如果返回Poll::Ready ,计时器被扔掉, future 成功。如果返回Poll::Pending , tokio等待下一个事件,即唤醒您的 future 或计时器。
  • 如果 future 醒来,tokio再次进行民意调查。如果返回Poll::Ready - 再次,计时器被扔掉, future 成功。
  • 如果定时器唤醒,tokio最后一次对 future 进行投票;如果还是Poll::Pending ,它超时并且不再轮询,并且 timeout返回错误。

但是,就您而言, future 不会返回 Poll::Pending - 它阻塞在 thread::sleep 内。因此,即使计时器可能在一秒过去后触发,tokio无法使用react - 它等待 future 返回,future 仅在线程解除阻塞后返回,并且,因为没有 await在 block 内,它返回 Poll::Ready - 所以计时器甚至没有被检查。

要解决此问题,您需要使用 tokio::time::sleep 对于异步代码内的任何暂停。有了它, future 就可以正确地超时。为了说明这一主张,让我们看一下与原始代码等效的独立示例:

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();
}

Playground

正如您已经注意到的,这失败了 - unwrap_err调用时出现 panic 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();
}

...我们得到了预期的行为 - playground .

关于rust - 为什么任务超时时不 panic ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71537671/

相关文章:

rust - 如何关闭已修改并正在执行的 `futures::sync::mpsc::Receiver` 流?

android - 用于Android的Tokio和Reqwest的Rust JNI异步回调

rust - 跨多个异步调用超时的最佳方法?

rust - 使用 Rust 解引用运算符 &* vs * with Self?

macros - 有没有办法用宏来计数?

async-await - 如何在新的 future API 中删除 future 的类型?

async-await - 如何使用 Rust 中新的异步等待语法通过 H2 执行 HTTP2 请求?

rust - "cannot move out of borrowed content"运算符重载

rust - 什么时候将成员值(value)从固定的 future 中移出是安全的?

rust - 在 rust/tokio 中合并流