如何冻结一个线程并从另一个线程通知它?

7

我需要在Rust中暂停当前线程并从另一个线程进行通知。在Java中,我会这样写:

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

并从第二个线程(以恢复主线程):

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

能否在Rust中实现相同的功能?

1
请注意,在共享标志上使用wait()/notify()而没有while循环是一个不好的想法 - wait()可能会停止等待,而没有调用notify(),这被称为虚假唤醒。使用专门的原语,如CountDownLatchExchanger<Void>将会更好;在下面的答案中,它与Rust中的通道非常相似。 - Vladimir Matveev
4个回答

10
使用发送类型为 () 的通道可能是最简单的:
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
< p > () 类型是因为它实际上没有任何信息,这意味着很明显你只是用它作为一个信号。它大小为零的事实也意味着在某些情况下它可能更快(但实际上可能不比正常的机器字写入更快)。< /p> < p >如果您只需要通知程序线程已经完成,您可以获取其 join guard 并等待其加入。< /p>
let guard = thread::spawn( ... ); // This will automatically join when finished computing

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

3
你可以使用std::thread::park()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之类的工具。

2
有多种方法可以在Rust中实现这一点。
在Java中的底层模型是每个对象都包含一个互斥锁和条件变量,如果我没记错的话。因此,使用互斥锁和条件变量也可以实现...
...然而,我个人会转而使用通道:
  • "等待"线程拥有通道的接收端,并等待它
  • "通知"线程拥有通道的发送端,并发送消息
这比条件变量更容易操作,尤其是因为在锁定变量时没有意外使用不同的互斥锁的风险。 std::sync::mpsc有两个通道(异步和同步),取决于您的需求。在这里,异步通道更加匹配:std::sync::mpsc::channel

请注意,使用通道时只能有一个等待线程。如果您需要在多个线程中等待并同时启动所有线程,则通道不适用。 - Mikko Rantalainen
1
@MikkoRantalainen:MPSC通道确实不行。广播通道可以工作,但线程可能不会完全同时启动。为了让所有线程一起启动,你能得到的最小延迟是原子和忙等待...但这有其他缺点。 - Matthieu M.

2

有一个监控的板条箱,通过将MutexCondvar结合在一个方便的结构中提供此功能。

(完全披露:我是作者。)

简而言之,可以像这样使用:

    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) {...}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接