Rust中为线程或函数创建超时的正确方法是什么?

nom7f22z  于 2022-11-12  发布在  其他
关注(0)|答案(3)|浏览(223)

这是我的代码:

use std::net;
use std::thread;

fn scan_port(host: &str, port: u16) -> bool {
    let host = host.to_string();
    let port = port;
    let t = thread::spawn(move || net::TcpStream::connect((host.as_str(), port)).is_ok());

    t.join().unwrap()
}

如何创建这样一种情况,即如果连接在N秒内没有完成,线程将被终止或终止?
这一切的原因是Rust没有办法设置套接字连接超时,所以我没有办法确保程序不会卡住。

qlckcl4x

qlckcl4x1#

正如@Shepmaster所指出的:终止线程不是一个好主意。
您可以改为给予线程一个Sender,如果它成功地打开了一个连接,它应该通过它通知您(甚至可以通过发送句柄)。然后您可以让主线程sleep等待您希望的时间。当您的线程唤醒时,它检查其对应的Receiver以寻找来自该线程的某种生命迹象。只有release it into the wild by dropping the JoinHandleReceiver。它不会消耗cpu时间(它被阻塞了),也不会消耗太多内存。如果它解除阻塞,它会检测到Sender没有连接,并会永久关闭。
当然,您不应该有大量的这些打开的线程,因为它们仍然使用资源(内存和系统线程句柄),但在正常的系统上,这不是太大的问题。
示例:

use std::net;
use std::thread;
use std::sync::mpsc;

fn scan_port(host: &str, port: u16) -> bool {
    let host = host.to_string();
    let port = port;
    let (sender, receiver) = mpsc::channel();
    let t = thread::spawn(move || {
        match sender.send(net::TcpStream::connect((host.as_str(), port))) {
            Ok(()) => {}, // everything good
            Err(_) => {}, // we have been released, don't panic
        }
    });

    thread::sleep(std::time::Duration::new(5, 0));

    match receiver.try_recv() {
        Ok(Ok(handle)) => true, // we have a connection
        Ok(Err(_)) => false, // connecting failed
        Err(mpsc::TryRecvError::Empty) => {
            drop(receiver);
            drop(t);
            // connecting took more than 5 seconds
            false
        },
        Err(mpsc::TryRecvError::Disconnected) => unreachable!(),
    }
}
dw1jzc5e

dw1jzc5e2#

@ker的回答总是等待5秒,即使连接完成得更快。下面是一个类似的方法,超时和网络请求 * 都 * 发生在不同的线程上,第一个完成的线程获胜:

let (sender, receiver) = mpsc::channel();
let tsender = sender.clone();
let t = thread::spawn(move || {
    match sender.send(Ok(net::TcpStream::connect((host.as_str(), port)))) {
        Ok(()) => {}, // everything good
        Err(_) => {}, // we have been released, don't panic
    }
});
let timer = thread::spawn(move || {
  thread::sleep(Duration::from_millis(5000));
  match tsender.send(Err(MyTimeoutError)) {
    Ok(()) => {}, // oops, we timed out
    Err(_) => {}, // great, the request finished already
  }
});
return receiver.recv().unwrap();

但是,只要您这样做,就可以直接使用recv_timeout

let (sender, receiver) = mpsc::channel();
let t = thread::spawn(move || {
    match sender.send(net::TcpStream::connect((host.as_str(), port))) {
        Ok(()) => {}, // everything good
        Err(_) => {}, // we have been released, don't panic
    }
});
return receiver.recv_timeout(Duration::from_millis(5000));
5lwkijsr

5lwkijsr3#

这是一个抽象,因此您可以运行任何具有超时的函数

use std::{
    collections::HashMap,
    sync::mpsc::{self, RecvTimeoutError},
    thread,
    time::Duration,
};

# [derive(Debug)]

struct TimeoutError;

fn run_with_timeout<F, T>(f: F, timeout: Duration) -> Result<T, TimeoutError>
where
    F: FnOnce() -> T + Send + 'static,
    T: Send + 'static,
{
    let (tx, rx) = mpsc::channel();
    let _ = thread::spawn(move || {
        let result = f();
        match tx.send(result) {
            Ok(()) => {} // everything good
            Err(_) => {} // we have been released, don't panic
        }
    });

    match rx.recv_timeout(timeout) {
        Ok(result) => Ok(result),
        Err(RecvTimeoutError::Timeout) => Err(TimeoutError),
        Err(RecvTimeoutError::Disconnected) => unreachable!(),
    }
}

# [allow(dead_code)]

# [derive(Debug)]

struct Foo {
    bar: String,
    bar_vec: Vec<String>,
    bar_map: HashMap<String, String>,
}

fn return_foo() -> Foo {
    Foo {
        bar: "bar".to_string(),
        bar_vec: vec!["bar".to_string()],
        bar_map: [("bar".to_string(), "bar".to_string())]
            .iter()
            .cloned()
            .collect(),
    }
}

fn main() {
    // This will timeout
    let result = run_with_timeout(
        || {
            thread::sleep(Duration::from_secs(2));
            42
        },
        Duration::from_secs(1),
    );
    println!("Result: {:?}", result);

    // This will not timeout
    let result = run_with_timeout(
        || {
            thread::sleep(Duration::from_secs(2));
            42
        },
        Duration::from_secs(3),
    );
    println!("Result: {:?}", result);

    // This will timeout (Custom type)
    let result = run_with_timeout(
        || {
            thread::sleep(Duration::from_secs(2));
            return_foo()
        },
        Duration::from_secs(1),
    );
    println!("Result: {:?}", result);

    // This will not timeout (Custom type)
    let result = run_with_timeout(
        || {
            thread::sleep(Duration::from_secs(2));
            return_foo()
        },
        Duration::from_secs(3),
    );
    println!("Result: {:?}", result);
}

相关问题