2016-06-22 20 views
2

Ich muss den aktuellen Thread in Rust anhalten und es von einem anderen Thread benachrichtigen. In Java würde ich schreiben:Wie man einen Thread einfriert und es von einem anderen benachrichtige?

synchronized(myThread) { 
    myThread.wait(); 
} 

und aus dem zweiten Thread (Haupt-Thread wieder aufnehmen):

synchronized(myThread){ 
    myThread.notify(); 
} 

Ist ist möglich, das gleiche in Rust zu tun?

+1

Beachten Sie, dass die Verwendung von 'wait()/notify()' ohne eine 'while' -Schleife auf einem gemeinsamen Flag eine schlechte Idee ist -' wait() 'kann nicht mehr warten, ohne dass' notify() 'aufgerufen wird als ein unechter Weckruf. Die Verwendung von spezialisierten Grundelementen wie "CountDownLatch" oder "Exchanger " wäre viel besser; es wäre den Kanälen in Rust in den Antworten unten sehr ähnlich. –

Antwort

5

einen Kanal verwenden, die () sendet Typ ist wahrscheinlich am einfachsten:

use std::sync::mpsc::channel; 
use std::thread; 

let (tx,rx) = channel(); 

// Spawn your worker thread, giving it `send` and whatever else it needs 
thread::spawn(move|| { 
    // Do whatever 
    tx.send(()).expect("Could not send signal on channel."); 
    // Continue 
}); 

// Do whatever 
rx.recv().expect("Could not receive from channel."); 
// Continue working 

Der () Typ ist, weil es effektiv Null-Information ist, die es ist ziemlich klar bedeutet, Sie verwenden sie nur als ein Signal. Die Tatsache, dass sie die Größe Null hat, bedeutet, dass sie in einigen Szenarien möglicherweise auch schneller ist (aber realistischerweise wahrscheinlich nicht schneller als ein normaler Maschinenwortschreibvorgang).

Wenn Sie nur das Programm benachrichtigen müssen, dass ein Thread fertig ist, können Sie seinen Join Guard holen und darauf warten, dass er sich anschließt.

let guard = thread::spawn(...); // This will automatically join when finished computing 

guard.join().expect("Could not join thread"); 
2

Es gibt mehrere Möglichkeiten, dies in Rust zu erreichen.

Das zugrunde liegende Modell in Java ist, dass jedes Objekt sowohl eine Mutex- als auch eine Zustandsvariable enthält, wenn ich mich richtig erinnere. So mit einem Mutex und condition variable funktionieren würde ...

... aber ich persönlich würde wechseln mit einem Kanal statt:

  • der „Warten“ Gewinde haben das Empfangsende des Kanals, und für wartet es
  • die „Benachrichtigung“ Gewinde hat das sendende Ende des Kanals, und sendet eine Nachricht

es einfacher ist, als eine Zustandsvariable zu manipulieren, insbesondere, weil es kein Risiko, versehentlich eine andere zu verwenden, Mutex beim Sperren der Variablen.

Die std::sync::mpsc hat zwei Kanäle (asynchron und synchron), je nach Ihren Bedürfnissen. Hier stimmt die asynchrone überein: std::sync::mpsc::channel.

+0

Beachten Sie, dass Sie einen Kanal vom Typ '()' verwenden können, der als "Signaltyp" gut funktioniert, da er im Grunde nur sagt "Ich sende eine Nachricht, die nichts bedeutet". – LinearZoetrope

+0

@Jsor: In der Tat. Gibt es einen Grund, warum du deine Antwort gelöscht hast? Es sieht gut aus. –

+0

Sie haben ziemlich genau die gleichen Dinge behandelt, die ich gemacht habe + ein wenig mehr. Außerdem war der Code auf dem Playground abgelaufen, und ich war mir nicht ganz sicher, warum, denn es ist im Grunde der Kanal-Beispielcode und wollte keinen fehlerhaften Code in einer Antwort. – LinearZoetrope

2

Sie können dazu std::thread::park() und std::thread::Thread::unpark() verwenden.

In dem Thread Sie warten wollen,

fn worker_thread() { 
    std::thread::park(); 
} 

im Controlling-Thread, der bereits

fn main_thread(worker_thread: std::thread::Thread) { 
    worker_thread.unpark(); 
} 

Beachten Sie, dass der Park Thread aufwecken kann spuriously, was bedeutet, ein Gewinde Griff Der Thread kann manchmal ohne die anderen Threads aufwachen, die unpark aufrufen. Sie sollten sich in Ihrem Code auf diese Situation vorbereiten oder etwas wie std::sync::mpsc::channel verwenden, das in @ Jsors Antwort vorgeschlagen wird.

0

Es gibt eine monitor Kiste, die diese Funktionalität bietet, indem Mutex mit Condvar in einer Convenience-Struktur kombiniert wird.

(Full Disclosure: Ich bin der Autor.)

Kurz gesagt, kann es wie folgt verwendet werden:

let mon = Arc::new(Monitor::new(false)); 
    { 
     let mon = mon.clone(); 
     let _ = thread::spawn(move || { 
      thread::sleep(Duration::from_millis(1000)); 

      mon.with_lock(|mut done| {  // done is a monitor::MonitorGuard<bool> 
       *done = true; 
       done.notify_one(); 
      }); 
     }); 
    } 

    mon.with_lock(|mut done| { 
     while !*done { 
      done.wait(); 
     } 
     println!("finished waiting"); 
    }); 

Hier mon.with_lock(...)synchronized(mon) {...} auf Java semantisch äquivalent ist.