为什么Condvar没有唤醒最后一个帖子?

问题描述 投票:0回答:2

当我和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,它实际上会唤醒所有九个循环。

我在the documentationwait()上双重检查了notify_one(),但这里的问题并不明显。

有什么想法吗?这是一个错误还是我做得不对的事情?

multithreading rust
2个回答
4
投票

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

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

(重点是我的)

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

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

这个问题的一个常见解决方案是收集JoinHandle返回的spawns,并根据sn99's answer等待它们。


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程序就会退出,即使其他线程仍在运行。

© www.soinside.com 2019 - 2024. All rights reserved.