如何冻结一个线程并从另一个线程通知它?
How to freeze a thread and notify it from another?
我需要在 Rust 中暂停当前线程并从另一个线程通知它。在 Java 我会写:
synchronized(myThread) {
myThread.wait();
}
从第二个线程(恢复主线程):
synchronized(myThread){
myThread.notify();
}
是否可以在 Rust 中做同样的事情?
使用发送类型 ()
的通道可能是最简单的:
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
()
类型是因为它实际上是零信息,这意味着很明显您只是将它用作信号。它的大小为零这一事实意味着它在某些情况下也可能更快(但实际上可能不会比正常的机器字写入快)。
如果你只是需要通知程序一个线程完成,你可以抓住它的join guard等待它加入。
let guard = thread::spawn( ... ); // This will automatically join when finished computing
guard.join().expect("Could not join thread");
在 Rust 中有多种方法可以实现这一点。
如果我没记错的话,Java 中的基础模型是每个对象都包含一个互斥体和一个条件变量。所以使用互斥锁和 condition variable 会起作用...
...但是,我个人会改为使用频道:
- "waiting"线程有通道的接收端,等待
- "notifying"线程有通道的发送端,发送消息
它比条件变量更容易操作,特别是因为在锁定变量时不存在意外使用不同互斥锁的风险。
std::sync::mpsc
has two channels (asynchronous and synchronous) depending on your needs. Here, the asynchronous one matches more closely: std::sync::mpsc::channel
.
您可以使用 std::thread::park() and std::thread::Thread::unpark() 来实现。
在您要等待的线程中,
fn worker_thread() {
std::thread::park();
}
在已经有线程句柄的控制线程中,
fn main_thread(worker_thread: std::thread::Thread) {
worker_thread.unpark();
}
请注意,停放线程可能会被虚假唤醒,这意味着该线程有时会在没有任何其他线程对其调用 unpark
的情况下唤醒。您应该在代码中为这种情况做好准备,或者使用接受的答案中建议的 std::sync::mpsc::channel 之类的东西。
有一个 monitor crate 通过在一个方便的结构中组合 Mutex
和 Condvar
来提供此功能。
(完全公开:我是作者。)
简而言之,可以这样使用:
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");
});
这里,mon.with_lock(...)
在语义上等同于 Java 的 synchronized(mon) {...}
。
我需要在 Rust 中暂停当前线程并从另一个线程通知它。在 Java 我会写:
synchronized(myThread) {
myThread.wait();
}
从第二个线程(恢复主线程):
synchronized(myThread){
myThread.notify();
}
是否可以在 Rust 中做同样的事情?
使用发送类型 ()
的通道可能是最简单的:
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
()
类型是因为它实际上是零信息,这意味着很明显您只是将它用作信号。它的大小为零这一事实意味着它在某些情况下也可能更快(但实际上可能不会比正常的机器字写入快)。
如果你只是需要通知程序一个线程完成,你可以抓住它的join guard等待它加入。
let guard = thread::spawn( ... ); // This will automatically join when finished computing
guard.join().expect("Could not join thread");
在 Rust 中有多种方法可以实现这一点。
如果我没记错的话,Java 中的基础模型是每个对象都包含一个互斥体和一个条件变量。所以使用互斥锁和 condition variable 会起作用...
...但是,我个人会改为使用频道:
- "waiting"线程有通道的接收端,等待
- "notifying"线程有通道的发送端,发送消息
它比条件变量更容易操作,特别是因为在锁定变量时不存在意外使用不同互斥锁的风险。
std::sync::mpsc
has two channels (asynchronous and synchronous) depending on your needs. Here, the asynchronous one matches more closely: std::sync::mpsc::channel
.
您可以使用 std::thread::park() and std::thread::Thread::unpark() 来实现。
在您要等待的线程中,
fn worker_thread() {
std::thread::park();
}
在已经有线程句柄的控制线程中,
fn main_thread(worker_thread: std::thread::Thread) {
worker_thread.unpark();
}
请注意,停放线程可能会被虚假唤醒,这意味着该线程有时会在没有任何其他线程对其调用 unpark
的情况下唤醒。您应该在代码中为这种情况做好准备,或者使用接受的答案中建议的 std::sync::mpsc::channel 之类的东西。
有一个 monitor crate 通过在一个方便的结构中组合 Mutex
和 Condvar
来提供此功能。
(完全公开:我是作者。)
简而言之,可以这样使用:
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");
});
这里,mon.with_lock(...)
在语义上等同于 Java 的 synchronized(mon) {...}
。