Почему Кондвар не пробуждает последний поток? - PullRequest
0 голосов
/ 02 апреля 2019

У меня неожиданные результаты при игре с 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, этона самом деле просыпаются все девять.

Я дважды проверил документацию на wait() и notify_one(), но не очевидно, что здесь не так.

Любоймысли?Это ошибка или что-то, что я не правильно делаю?

Ответы [ 2 ]

4 голосов
/ 02 апреля 2019

Ваш основной поток не ожидает завершения рабочих потоков, и согласно документации std::thread::spawn:

[…] дочерний поток может пережитьparent (, если только родительский поток не является основным потоком; весь процесс завершается, когда основной поток завершает ).

(выделение мое)

Потому чтоваша программа завершается сразу после последнего notify_one, рабочий поток, вероятно, будет прерван до печати значения.

Когда вы выполняете цикл 11 раз, после 10-го notify_all наступает спящий режим, равный 1 с, что означаетпоследний рабочий поток, скорее всего, завершит свою работу.

Распространенным решением проблемы является сбор JoinHandle s, возвращаемых spawn, и ожидание их согласно ответу sn99 .

1 голос
/ 02 апреля 2019

Измените код на:

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 завершается, как только возвращается main, даже если другие потоки все еще работают.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...