Поток std::thread запускается в объекте, когда он завершается?

Если я раскручиваю std::thread в конструкторе Bar когда он перестает работать? Гарантируется ли остановка, когда Bar экземпляр разрушен?

class Bar {

public:

Bar() : thread(&Bar:foo, this) {
}

...

void foo() {
  while (true) {//do stuff//}

}

private:
  std::thread thread;

};

РЕДАКТИРОВАТЬ: Как мне правильно прекратить std::thread в деструкторе?

3 ответа

Если я раскручиваю std::thread в конструкторе Bar, когда он перестает работать?

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

Гарантируется ли остановка при разрушении экземпляра Bar?

Нет. Чтобы гарантировать это, позвоните std::thread::join в Bar деструктор.

На самом деле, если бы ты не позвонил thread::join или же thread::detach до Bar::~Barчем ваше заявление будет прекращено путем автоматического вызова std::terminate, так что вы должны позвонить либо join (предпочтительно) или detach (менее рекомендуется).

ты тоже хочешь позвонить therad::join на деструкторе объекта, потому что порожденный поток полагается на живой объект, если объект разрушен, когда ваш поток работает с этим объектом - вы используете разрушенный объект и у вас будет неопределенное поведение в вашем коде.

Средства потоков C++ не включают в себя встроенный механизм завершения потока. Вместо этого вы должны решить для себя: a) механизм, сигнализирующий потоку о том, что он должен завершиться, b) что вас не волнует прерывание потока в середине операции, когда процесс завершается, а операционная система просто перестает запускать свои потоки. Больше.

std::thread Объект не сам поток, а непрозрачный объект, содержащий дескриптор / дескриптор потока, поэтому теоретически он может быть уничтожен без влияния на поток, и были аргументы за и против автоматического завершения самого потока. Вместо этого, в качестве компромисса, было сделано так, чтобы уничтожить std::thread объект, в то время как поток оставался работающим и подключенным, приведет к завершению приложения.

В результате в деструкторе есть такой код:

~thread() {
    if (this->joinable())
        std::terminate(...);
}

Вот пример использования простой атомарной переменной и проверки ее в потоке. В более сложных случаях вам может понадобиться condition_variable или другой более сложный механизм сигнализации.

#include <thread>
#include <atomic>
#include <chrono>
#include <iostream>

class S {
    std::atomic<bool> running_;
    std::thread thread_;
public:
    S() : running_(true), thread_([this] () { work(); }) {}
    void cancel() { running_ = false; }
    ~S() {
        if ( running_ )
            cancel();
        if ( thread_.joinable() )
            thread_.join();
    }
private:
    void work() {
        while ( running_ ) {
            std::this_thread::sleep_for(std::chrono::milliseconds(500));
            std::cout << "tick ...\n";
            std::this_thread::sleep_for(std::chrono::milliseconds(500));
            std::cout << "... tock\n";
        }
        std::cout << "!running\n";
    }
};

int main()
{
    std::cout << "main()\n";
    {
        S s;
        std::this_thread::sleep_for(std::chrono::milliseconds(2750));
        std::cout << "end of main, should see a tock and then end\n";
    }
    std::cout << "finished\n";
}

Демонстрация в реальном времени: http://coliru.stacked-crooked.com/a/3b179f0f9f8bc2e1

Краткий ответ: да и нет. Да, поток заканчивается, но не обычным способом (убивая поток), а основным потоком, выходящим из-за std::terminate вызов.

Длинный ответ: Поток может быть безопасно разрушен только после завершения основной функции (потока). Это можно сделать двумя способами

  • призвание join(), которая ожидает завершения потока (в вашем случае, никогда)
  • призвание detach(), который отсоединяет поток от основного потока (в этом случае поток заканчивается, когда основной поток закрывается - когда программа завершается).

Если деструктор вызывается, если все эти условия не применяются, то std::terminate называется:

  • он был построен по умолчанию

  • оно было перенесено из

  • join() был назван

  • detach() был назван

Другие вопросы по тегам