为什么Condvar不唤醒最后一个线程?

时间:2019-04-01 21:54:43

标签: multithreading rust

while (line != null) { for (int i = 0; i < line.length(); i++) { if (!Character.isWhitespace(line.charAt(i))) count ++; line = br.readLine(); } System.out.println(count); } 时出现意外结果。我知道我不敢相信自己的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();
    }
}

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

我在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! wait()上都对the documentation进行了仔细检查,但这里出了什么问题并不明显。

有什么想法吗?这是错误还是我没有做对的事情?

2 个答案:

答案 0 :(得分:4)

根据the documentation of std::thread::spawn,您的主线程不等待工作线程完成:

  

[…]子线程可能会超过父线程(,除非父线程是主线程;整个过程在主线程结束时终止。)

(强调是我的)

由于您的程序在最后一个notify_one之后立即终止,因此工作线程可能会在打印值之前被杀死。

当您循环11次时,第10个notify_all之后会休眠1 s,这意味着最新的工作线程可能会完成其工作。

该问题的常见解决方案是按照sn99's answer收集JoinHandle返回的spawn,并等待它们。

答案 1 :(得分:1)

将代码更改为:

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

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

    // Create a vector of `JoinHandle` 
    let mut thread_handles = Vec::new();

    for id in 0..10 {
        let pair2 = pair.clone();
        // Push JoinHandle in the vector
        thread_handles.push(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();
    }

    // Wait for all threads to complete before exiting `main`
    // handles is of type JoinHandle
    for handles in thread_handles {
        handles.join().unwrap();
    }
}

join方法等待所有线程完成。这是必要的,因为即使其他线程仍在运行,Rust程序也会在主程序返回后立即退出。