2014-10-05 13 views
7

Zrobiłem wątek z nieskończoną pętlą i zegar wewnątrz.Jak zakończyć lub zawiesić wątek Rust z innego wątku?

thread::spawn(|| { 
    let mut timer = Timer::new().unwrap(); 
    let periodic = timer.periodic(Duration::milliseconds(200)); 
    loop { 
     periodic.recv(); 

     // Do my work here 
    } 
}); 

pamiętać, że ten przykład został stworzony przed Rust 1.0 oraz szczególne rodzaje zostały zmienione lub usunięte, ponieważ wtedy, ogólne pytanie koncepcja pozostaje ważny

Po pewnym czasie na podstawie pewnych warunkach Muszę zakończyć ten wątek z innej części mojego programu. Innymi słowy po prostu chcę wyjść z nieskończonej pętli. Jak mogę to zrobić poprawnie? Dodatkowo, jak mogę zawiesić ten wątek i wznowić go później?

Próbowałem użyć globalnej flagi niebezpiecznej do przerwania pętli, ale myślę, że to rozwiązanie nie wygląda ładnie.

Odpowiedz

12

Edit: aktualizowana Rust 1.x

dla obu tych zadań (zakończyć i zawiesić wątku) można wykorzystać kanały.

Oto jak wątek może zostać rozwiązana zewnętrznie:

use std::thread; 
use std::time::Duration; 
use std::sync::mpsc::{self, TryRecvError}; 
use std::io::{self, BufRead}; 

fn main() { 
    println!("Press enter to terminate the child thread"); 
    let (tx, rx) = mpsc::channel(); 
    thread::spawn(move || { 
     loop { 
      println!("Working..."); 
      thread::sleep(Duration::from_millis(500)); 
      match rx.try_recv() { 
       Ok(_) | Err(TryRecvError::Disconnected) => { 
        println!("Terminating."); 
        break; 
       } 
       Err(TryRecvError::Empty) => {} 
      } 
     } 
    }); 

    let mut line = String::new(); 
    let stdin = io::stdin(); 
    let _ = stdin.lock().read_line(&mut line); 

    let _ = tx.send(()); 
} 

Oznacza to, że na każdej iteracji pętli pracownika możemy sprawdzić, czy ktoś powiadomił nas poprzez kanał. Jeśli tak lub jeśli drugi koniec kanału wykracza poza zakres, po prostu przerywamy pętlę.

Oto jak nitki może być „zawieszony” i „wznowić”:

use std::time::Duration; 
use std::thread; 
use std::sync::mpsc; 
use std::io::{self, BufRead}; 

fn main() { 
    println!("Press enter to wake up the child thread"); 
    let (tx, rx) = mpsc::channel(); 
    thread::spawn(move || { 
     loop { 
      println!("Suspending..."); 
      match rx.recv() { 
       Ok(_) => { 
        println!("Working..."); 
        thread::sleep(Duration::from_millis(500)); 
       } 
       Err(_) => { 
        println!("Terminating."); 
        break; 
       } 
      } 
     } 
    }); 

    let mut line = String::new(); 
    let stdin = io::stdin(); 
    for _ in 0..4 { 
     let _ = stdin.lock().read_line(&mut line); 
     let _ = tx.send(()); 
    } 
} 

Tutaj używamy recv() metodę, która zawiesza wątek aż coś przybywa na kanale, tak aby wznowić wątek ty wystarczy wysłać coś (wartość jednostki () w tym przypadku) przez kanał. Jeśli koniec transmisji kanału zostanie przerwany, recv() zwróci Err(()) - używamy tego, aby opuścić pętlę.

Kanały to najprostszy i najbardziej naturalny (IMO) sposób wykonywania tych zadań, ale nie najskuteczniejszy. Istnieją inne prymitywy współbieżności, które można znaleźć w module std::sync. Należą one do niższego poziomu niż kanały, ale mogą być bardziej wydajne w określonych zadaniach.

Powiązane problemy