在 Rust 中设置超时并中止计算。

6
我有一个 Rust 函数(不是我编写的),调用它会在几毫秒内返回结果,或者在 ~10 分钟后失败。
我想要封装这个函数调用,并返回一个 `Option`。如果函数运行时间超过 10 秒,则返回 `None`;如果函数运行时间小于 10 秒,则返回运行结果。但是一旦该函数被调用,我找不到中断其评估的任何方法。
例如:
// This is the unpredictable function
fn f() {
    // Wait randomly for between 0 and 10 seconds
    let mut rng = rand::thread_rng();
    std::thread::sleep(std::time::Duration::from_secs(rng.gen_range(0, 10)));
}

fn main() {
    for _ in 0..100 {
        // Run f() here but so that the whole loop takes no more than 100 seconds
        // by aborting f() if it takes longer than 1 second
    }
}

我找到了一些使用带有超时的futures的方法,但是我想尽量减少开销,并且我不确定针对每次调用该函数都创建一个future会有多大的代价,因为它将被调用很多次。

谢谢


1
这个回答解决了你的问题吗?在Rust中创建线程或函数的超时,正确的方法是什么? - SCappella
@SCappella 我看到了那个帖子,但我认为它并没有完全回答我的问题,因为那涉及到生成整个线程,这对于我所做的事情来说太昂贵了。我认为Peter在下面提出的解决方案通过使用future而不是线程来解决了这个问题。 - user42257
或者,只需解决停机问题 /s(显然是开玩笑的)。这是一个非常好的问题,@user42257。 - Ben Aubin
1个回答

4

异步执行的开销可能非常小,尤其是由于您的函数至少运行在毫秒级别上,这已经非常慢了。

像这样做就可以了:

use rand::Rng;
use std::time::Duration;
use tokio::time::timeout;

async fn f() -> i32 {
    // Wait randomly for between 0 and 10 seconds
    let mut rng = rand::thread_rng();
    tokio::time::delay_for(Duration::from_secs(rng.gen_range(0, 10))).await;
    // return something
    1000
}

#[tokio::main]
async fn main() {
    for _ in 0..100 {
        if let Ok(result) = timeout(Duration::from_secs(1), f()).await {
            println!("result = {}", result);
        } else {
            // took too long
        }
    }
}

在处理性能方面,如果你担心某种方法会很慢,请测试验证而不是仅仅猜测。性能特点通常会让人感到意外。

谢谢您的回复,但是在打印“result = 1000”之间等待超过1秒仍然是可以接受的。也就是说,我认为超时实际上并没有中止评估,并且else语句从未运行。 - user42257
@user42257 thread::sleep 是一个阻塞操作,因此不应在 async 函数中使用。这就是为什么它不能正常工作的原因。您的函数可能也是阻塞的,因此线程是唯一的选择。除非您的函数是为 async 使用而设计的,否则它将阻塞整个线程。 - SCappella
@SCappella 啊,我不知道tokio::time::timeout有这个限制。我会从你之前推荐的链接开始研究如何使用线程来解决这个问题。 - user42257
我的错!我盲目地复制了 OP 的代码,没有测试。现在我已经更新了我的答案。 - Peter Hall

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接