@maytilex

C++ | Почему новый поток со временем периодически перестаёт работать?

Это MyClass.hpp:

MyClass.hpp

class MyClass
{
public:
    MyClass(); ~MyClass();

    /*______ var ______*/

private:
    void run();

    /*______ var ______*/

    std::atomic<bool> exit_flag_dPManager = false;
    std::thread thr_dPManager;
};



Это MyClass.cpp:

MyClass.cpp

MyClass::MyClass() : exit_flag_dPManager(true), thr_dPManager(&MyClass::run, this)
{}

MyClass::~MyClass()
{
    if (this->exit_flag_dPManager)
    {
        this->exit_flag_dPManager = false;

        this->thr_dPManager.join();
    }
}



Суть в чём - поток, который запускается в момент создания экземпляра класса, работает как положено. В нём бесконечный цикл (поток завершается при определённых условиях), всё отрабатывает хорошо, считает как положено. Но вот не задача - он спустя одну минуту или две просто перестаёт делать то, что должен. Даже вывод printf() не выводит. Но опять же спустя несколько секунд/минуту запускается и продолжает работать/выводить и снова тухнет и с каждым разом период "сна" увеличивается. Чем дольше я смотрю на консоль, тем дольше у него период сна.

Это норма? Как с таким поведением бороться? Может где-то приоритет добавить?
  • Вопрос задан
  • 158 просмотров
Решения вопроса 1
@code_panik
Наиболее вероятная проблема - race condition (состояние гонки) + ub (неопределенное поведение) из-за проблем синхронизации основного потока, в котором вызывается деструктор ~MyClass, и потока thr_dPManager.
Пусть в основном потоке вызывается деструктор, но exit_flag_dPManager = false, тогда this->thr_dPManager.join(); не выполнится (race condition) и без ожидания завершения run() в основном потоке будут тихо освобождены все данные связанные с объектом класса и объектом потока thr_dPManager. В run() мы попытаемся обратиться к освобожденным данным класса (ub) и получим непредсказуемое поведение программы. В c++ для каждого потока мы должны корректно вызывать join или detach. Вынесем join из if,
MyClass::~MyClass()
{
    if (this->exit_flag_dPManager)
        this->exit_flag_dPManager = false;

    if (thr_dPManager.joinable())
        thr_dPManager.join();
}

Осталось проверить логику программы и убедиться, что "бесконечный" цикл завершится. Пока непонятно, зачем в деструкторе строка this->exit_flag_dPManager = false, ведь после завершения деструктора у нас не будет доступа к данным класса.
Если exit_flag_dPManager используется для синхронизации: завершить run, если завершается основной поток, — тогда можно писать
MyClass() : exit_flag_dPManager(false) ....

void run() {
  while (!exit_flag_dPManager.load()) { ... }
}

~MyClass() {
  this->exit_flag_dPManager = true;

  if (thr_dPManager.joinable())
    thr_dPManager.join();
}


Также нужно иметь в виду потенциальные проблемы из статьи https://habr.com/ru/articles/444464/.
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы