Указание std::thread убить/остановить себя при выполнении условия

Скажем, у меня есть рабочий поток tWorker, который инициализируется при построении Boss и сообщает ему выполнить work() до тех пор, пока bRetired не станет истинным. std::mutex, mtx блокирует некоторые данные (vFiles), чтобы tWorker владел ими, когда он с ними работает.

Как заставить tWorker "покончить жизнь самоубийством", когда bRetired станет true? Как mutex будет уничтожен, когда поток прекратит выполнение?

Я читал, что std::thread объекты никоим образом не могут быть прерваны. Дает ли потоку возможность ничего не делать (или вызов std::this_thread::yield()) тот же эффект, что и уничтожение потока?

class Boss {
private:
    std::thread tWorker;
    std::mutex mtx;
    bool bRetired;
    std::vector< std::string > vFiles;

    void work() {
        while ( bRetired == false ) {
            // Do your job!
            mtx.lock();
            // ... Do something about vFiles ...
            mtx.unlock();
        }

        // tWorker has retired, commit suicide
        // ** How? **

        // Does this suffice if I want to "kill" the thread?
        std::this_thread::yield(); 
    }

public:
    Boss() {
        bRetired = false;
        tWorker = std::thread( &Boss::work, this );

        // Have worker do its job independently
        // **Bonus Question** : Should this be tWorker.join() or tWorker.detach()?
        tWorker.detach();
    }

    retire() {
        bRetired = true;
    }
}

Примечания

  • Рабочий поток не может быть запущен снова после его вывода из эксплуатации.
  • Рабочий поток работает в фоновом режиме, не прерывая выполнение основного потока.

person alxcyl    schedule 18.09.2013    source источник


Ответы (2)


Вызов std::thread::yield() не требуется и не уничтожает вызывающий поток:

Предоставляет подсказку реализации перепланировать выполнение потоков, разрешая выполнение других потоков.

Просто выйдите из функции, чтобы выйти из потока.

Обратите внимание, что использование bRetired неверно, так как два потока могут обращаться к одному и тому же месту в памяти, и один из этих потоков изменяет его: это поведение undefined. Кроме того, изменение, сделанное в функции retire(), другом потоке, не будет видно потоку, выполняющему run(): используйте atomic<bool> для атомарности и видимости.

Если внутри конструктора используется join(), конструктор не вернется до тех пор, пока поток завершился, чего никогда бы не произошло, так как было бы невозможно вызвать retire(), потому что объект был бы недоступен (поскольку конструктор не вернулся бы). Если требуется синхронизация с выходом из потока, то делайте не detach(), а join() в функции retire():

void retire() {
    bRetired = true;
    tWorker.join();
}

Используйте RAII для получения mutexes (например, std::lock_guard), чтобы он всегда освобождался. mutex будет уничтожен, когда он выйдет за пределы области видимости, в данном случае, когда его содержащий класс будет уничтожен.

person hmjd    schedule 18.09.2013
comment
Итак, выход из цикла while остановит поток? Скажем, я добавил еще один bool, который является bSleep, чтобы заставить поток спать или ничего не делать, пока я не скажу (не через определенное время), должен ли я вернуться из функции или использовать yield()? - person alxcyl; 18.09.2013
comment
@LanceGray, см. condition_variable для реализации подождите, пока я вам не скажу сделать что-то механизм. - person hmjd; 18.09.2013
comment
Если я правильно понял, std::condition_variable похож на pthread_cond_wait. Это правильно? - person alxcyl; 18.09.2013
comment
Да, в зависимости от вашей ОС реализация может использовать что-то подобное внутри. - person Hulk; 18.09.2013

Как заставить tWorker «покончить жизнь самоубийством», когда значение bRetired становится истинным?

Вы позволяете потоку управления выйти из функции потока. Этот std::this_thread::yield() звонок ненужный.

Как будет уничтожен мьютекс, когда поток прекратит выполнение?

Этот мьютекс является членом класса Boss. Он уничтожается в деструкторе Boss, когда объект уничтожается.

Я читал, что объекты std::thread никоим образом не могут быть прерваны.

C++ API не предоставляет средств для завершения произвольного потока. Должен быть способ приказать потоку завершиться, а затем подождать, пока он не завершится, как вы и намеревались сделать.

Позволить потоку ничего не делать (или вызвать std::this_thread::yield()) дает тот же эффект, что и уничтожение потока?

No.

Однако есть состояние гонки для переменной bRetired. Либо он должен быть std::atomic<bool>, либо его следует читать и изменять только тогда, когда этот мьютекс заблокирован.

person Maxim Egorushkin    schedule 18.09.2013