首页 > 解决方案 > 为什么 Condvar 不唤醒最后一个线程?

问题描述

玩弄时我得到了意想不到的结果Condvar。我知道我不能相信我wait()不会早起,但在我的情况下,我的一个唤醒似乎一直缺失。鉴于此示例代码:

use std::sync::{Arc, Mutex, Condvar};
use std::{
    thread,
    time
};

fn main() {
    let pair = Arc::new((Mutex::new(false), Condvar::new()));
    let pause = time::Duration::from_secs(1);

    for id in 0..10 {
        let pair2 = pair.clone();
        thread::spawn(move|| {
            let &(ref lock, ref cvar) = &*pair2;

            let lock = lock.lock().unwrap();
            let _ = cvar.wait(lock).unwrap();
            println!("Thread {} done!", id);
        });
    }

    // Wait for the thread to start up.
    let &(ref _lock, ref cvar) = &*pair;

    for _ in 0..10 {
        thread::sleep(pause);
        cvar.notify_one();
    }
}

我只从循环中取出前八个线程:

Thread 0 done!
Thread 1 done!
Thread 2 done!
Thread 3 done!
Thread 4 done!
Thread 5 done!
Thread 6 done!
Thread 7 done!
Thread 8 done!

如果我将第二个循环的计数增加到 11,它实际上会唤醒所有 9 个。

我已经仔细检查了两者的文档wait()notify_one()但这里有什么问题并不明显。

有什么想法吗?这是一个错误还是我做错了什么?

标签: multithreadingrust

解决方案


您的主线程不会等待工作线程完成,并且根据以下文档std::thread::spawn

[...] 子线程可能比父线程寿命更长(除非父线程是主线程;整个进程在主线程完成时终止)。

(重点是我的)

因为您的程序在 last 之后立即终止notify_one,所以工作线程可能会在打印值之前被杀死。

当你循环 11 次时,在 10th 之后会有 1s 的睡眠notify_all,这意味着最新的工作线程可能会完成它的工作。

该问题的一个常见解决方案是收集JoinHandles 返回的 s,并按照sn99 的回答spawn等待它们。


推荐阅读