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

时间:2016-03-23 15:03:32

标签: multithreading rust

这是我的代码:

use std::net;
use std::thread;
extern crate argparse;
use argparse::{ArgumentParser, StoreTrue, Store};

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无法设置套接字连接超时,因此我无法确保程序不会卡住。

2 个答案:

答案 0 :(得分:11)

正如@Shepmaster所说:终止线程是一个坏主意。

你可以做的是给线程一个Sender,如果它已成功打开一个连接(甚至可能通过发送你的句柄),它应该通知你。然后你可以让你的主线程sleep等待你想要等待的时间。当你的线程被唤醒时,它会从线程中检查相应的Receiver以查找生命迹象。如果主题没有回答,只需release it into the wild by dropping the JoinHandleReceiver。它不像消耗cpu时间(它被阻止),而且它不会消耗太多内存。如果它解锁,它会检测到Sender没有连接,并且可以关闭。

当然你不应该有这些开放线程的bazillions,因为它们仍然使用资源(内存和系统线程句柄),但是在一个不是太大问题的普通系统上。

示例:

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

答案 1 :(得分:3)

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