2016-03-23 18 views
5

Questo è il mio codice:Qual è il modo corretto in Rust di creare un timeout per un thread o una funzione?

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

Come si crea una situazione in cui il filo sarà terminata o uccisi se la connessione non ha terminato in N secondi?

Il motivo di tutto questo è che Rust non ha modo di impostare un timeout della connessione socket quindi non ho modo di garantire che il programma non si blocchi.

+3

Probabilmente sarebbe meglio se il codice di esempio fosse più minimale, mostrando solo il problema a portata di mano invece di una pagina di parsing dell'argomento della riga di comando e di opzioni di manipolazione. –

+0

Terminare o uccidere un thread era un [cattivo] (http://stackoverflow.com/q/5218969/155423) [idea] (http://stackoverflow.com/q/1559255/155423) nelle lingue che l'hanno aggiunto . Ruggine non lo aggiungerà. – Shepmaster

+0

@MikeCooper Fatto :) – Ba7a7chy

risposta

8

Come ha osservato @Shepmaster: è una cattiva idea terminare i thread.

Ciò che si può fare è dare il thread a Sender tramite il quale dovrebbe notificare se ha aperto correttamente una connessione (forse anche inviando l'handle). Quindi puoi lasciare il thread principale sleep per il tempo che desideri attendere. Quando il tuo thread si attiva, controlla il suo corrispondente Receiver per qualche segno di vita dal thread. Nel caso in cui il thread non ha risposto, solo release it into the wild by dropping the JoinHandle e Receiver. Non è che stia consumando cpu-time (è bloccato) e non sta consumando troppa memoria. Se dovesse mai sbloccarsi, verrà rilevato che lo Sender non è connesso e può essere spento per sempre.

Ovviamente non dovresti avere miliardi di questi thread aperti, perché usano ancora risorse (handle di thread di memoria e di sistema), ma su un sistema normale che non è un problema.

Esempio:

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

Ho provato che ora, sembra che il ricevitore blocchi fino a quando non ottiene una risposta, se questo è davvero il caso mi trovo nello stesso posto quando utilizzo Send \ Receive – Ba7a7chy

+2

ho aggiornato con un po 'di codice. Devi usare 'try_recv', che è il metodo di ricezione non bloccante –

0

La risposta da @ker sarà sempre attendere 5 secondi, anche se la connessione termina in modo più rapido. Ecco un approccio simile in cui il timeout e la rete di richiesta sia accadono sul thread separati, e il primo che vince finito:

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

Ma finché si sta facendo questo, si potrebbe anche solo usare recv_timeout invece:

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