Запускайте цикл до тех пор, пока будущее не закончится или не закончится время ожидания

#rust #timeout #tokio

Вопрос:

Я пытаюсь реализовать базовый алгоритм итеративного углубления в Rust:

 pub async fn iterative_best_move(pos: amp;mut Position, col: PieceColour, timeout: u64) -> (Option<movegen::Move>, i32) {
    let mut ret = (None, -1);
    let mut time_remaining = std::time::Duration::from_secs(timeout);
    let mut current_depth = 3;

    loop {
        let start = time::Instant::now();

        let mut pos = pos.shallow_clone();

        let x = tokio::time::timeout(time_remaining, async move {
            depth_best_move(amp;mut pos, col, current_depth).await
        }).await;

        if let Err(_) = x {
            // We ran out of time!
            println!("Time's up!");
            break;
        }

        ret = x.unwrap().unwrap();

        time_remaining -= start.elapsed();
        current_depth  = 1;
    }

    println!("Depth {} in {} seconds.", current_depth, timeout);

    ret
}
 

Проблема с моей реализацией заключается в том, что она, похоже, никогда не истекает по времени, вместо этого переходя к следующей итерации цикла и вызывая панику, когда она пытается представить отрицательную продолжительность.

Функция вызывается из main примерно так:

 #[tokio::main]
async fn main() {
    let mut pos = Position::random_legal();

    let start = time::Instant::now();

    search::iterative_best_move(amp;mut pos, piece::PieceColour::White, 5).await;
}
 

Почему функция не выполняет тайм-аут и не возвращается, как следовало бы?

Комментарии:

1. Я не думаю, что продолжительность может быть отрицательной, не могли бы вы добавить какой-нибудь журнал выполнения?

2. @Netwave Продолжительность действительно не может быть отрицательной. Однако это не должно иметь значения, так как условие if приведет к тому, что программа выйдет из цикла до того, как будет вычтено значение time_remaining.

3. да, но тогда что ты имеешь в виду? Произойдет ли это с ошибкой паники, если вместо этого вы используете фиксированный тайм-аут 0?

4. Во — первых, почему вы используете async для этой проблемы ? Во — вторых, нет никакой гарантии, что ваше будущее возобновится сразу же после .await точки. Возможно, что в данный момент среда выполнения работает в другом будущем, и вам придется подождать. Таким образом start.elapsed() , необязательно отражать фактическое время, проведенное в depth_best_move() . ТАК что вы должны использовать doc.rust-lang.org/std/time/… во избежание незаполнения

5. @DallasHewitt Это не так tokio::time::timeout работает. В общем, фьючерсы работают только тогда, когда они опрошены, таким образом timeout , опрашивается будущее, и если оно завершено, то оно возвращает результат, в противном случае оно опрашивает таймер и решает, отбросит ли оно будущее или нет. Но если будущее будет сложным с точки зрения вычислений, то опрос будет заблокирован на долгое время, поэтому тайм-аут никогда не сработает, это может занять больше времени, чем настроено для значения тайм-аута. Асинхронность/ожидание-это совместная модель, а не упреждающая