简体   繁体   English

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

[英]What is the correct way in Rust to create a timeout for a thread or a function?

This is my code:这是我的代码:

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

How do I create a situation where the thread will be terminated or killed if the connection didn't finish in N seconds?如果连接未在 N 秒内完成,如何创建线程将被终止或终止的情况?

The reason for all of this is that Rust has no way to set a socket connection timeout so I have no way to ensure the program won't get stuck.所有这一切的原因是 Rust 无法设置套接字连接超时,所以我无法确保程序不会卡住。

As @Shepmaster noted: it's a bad idea to terminate threads.正如@Shepmaster 指出的那样:终止线程是个坏主意。

What you can do instead is to give the thread a Sender through which it should notify you if it has successfully opened a connection (maybe even by sending you the handle).相反,您可以做的是为线程提供一个Sender ,如果它已成功打开连接,它应该通过它通知您(甚至可能通过向您发送句柄)。 Then you can let your main thread sleep for the time you wish to wait.然后你可以让你的主线程在你希望等待的时间内sleep When your thread wakes up, it checks its corresponding Receiver for some sign of life from the thread.当您的线程唤醒时,它会检查其相应的Receiver是否有来自线程的一些生命迹象。 In case the thread did not answer, just release it into the wild by dropping the JoinHandle and the Receiver .如果线程没有回答,只需通过删除JoinHandleReceiver将其释放到野外即可 It's not like it's consuming cpu-time (it's blocked), and it's not consuming too much memory.它不像在消耗 cpu 时间(它被阻塞),也没有消耗太多内存。 If it ever unblocks, it'll detect that the Sender is not connected and can shut down for good.如果它解除阻塞,它将检测到Sender未连接并且可以永久关闭。

Of course you should not have bazillions of these open threads, because they still use resources (memory and system thread handles), but on a normal system that's not too much of an issue.当然,你不应该有无数的这些开放线程,因为它们仍然使用资源(内存和系统线程句柄),但在正常系统上这不是什么大问题。

Example:示例:

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!(),
    }
}

The answer by @ker will always wait 5 seconds, even if the connection finishes more quickly. @ker 的回答将始终等待 5 秒,即使连接完成得更快。 Here is a similar approach where the timeout and network request both happen on separate threads, and the first one finished wins:这是一种类似的方法,超时和网络请求发生在不同的线程上,第一个完成的获胜:

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

But as long as you're doing that, you might as well just use recv_timeout instead:但只要你这样做,你不妨使用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));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM