Создание нового потока, вызывающего исключение - PullRequest
0 голосов
/ 17 сентября 2018

У меня есть таймер, который создаст новый поток и будет ждать истечения таймера, прежде чем вызывать функцию уведомления. Он работает правильно во время первого выполнения, но когда таймер запускается во второй раз, выдается исключение при попытке создать новый поток. Выходные данные отладки показывают, что предыдущий поток завершил работу, прежде чем пытаться создать новый поток.

Timer.hpp:

 class TestTimer
{
private:
    std::atomic<bool> active;
    int timer_duration;
    std::thread thread;
    std::mutex mtx;
    std::condition_variable cv;
    void timer_func();
public:
    TestTimer() : active(false) {};
    ~TestTimer() {
        Stop();
    }
    TestTimer(const TestTimer&) = delete;               /* Remove the copy constructor */
    TestTimer(TestTimer&&) = delete;                    /* Remove the move constructor */
    TestTimer& operator=(const TestTimer&) & = delete;  /* Remove the copy assignment operator */
    TestTimer& operator=(TestTimer&&) & = delete;       /* Remove the move assignment operator */
    bool IsActive();
    void StartOnce(int TimerDurationInMS);
    void Stop();

    virtual void Notify() = 0;
};

Timer.cpp:

void TestTimer::timer_func()
{
    auto expire_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(timer_duration);
    std::unique_lock<std::mutex> lock{ mtx };
    while (active.load())
    {
        if (cv.wait_until(lock, expire_time) == std::cv_status::timeout)
        {
            lock.unlock();
            Notify();
            Stop();
            lock.lock();
        }
    }
}

bool TestTimer::IsActive()
{
    return active.load();
}

void TestTimer::StartOnce(int TimerDurationInMS)
{
    if (!active.load())
    {
        if (thread.joinable())
        {
            thread.join();
        }
        timer_duration = TimerDurationInMS;
        active.store(true);
        thread = std::thread(&TestTimer::timer_func, this);
    }
    else
    {
        Stop();
        StartOnce(TimerDurationInMS);
    }
}

void TestTimer::Stop()
{
    if (active.load())
    {
        std::lock_guard<std::mutex> _{ mtx };
        active.store(false);
        cv.notify_one();
    }
}

Ошибка выдается из моего блока кода здесь: thread = std::thread(&TestTimer::timer_func, this); во время второго исполнения.

В частности, ошибка вызывается из функции move_thread: _Thr = _Other._Thr;

thread& _Move_thread(thread& _Other)
        {   // move from _Other
        if (joinable())
            _XSTD terminate();
        _Thr = _Other._Thr;
        _Thr_set_null(_Other._Thr);
        return (*this);
        }

    _Thrd_t _Thr;
    }; 

И это исключение: Unhandled exception at 0x76ED550B (ucrtbase.dll) in Sandbox.exe: Fatal program exit requested.

Трассировка стека:

thread::move_thread(std::thread &_Other)
thread::operator=(std::thread &&_Other)
TestTimer::StartOnce(int TimerDurationInMS)

Ответы [ 2 ]

0 голосов
/ 17 сентября 2018

Вы создаете TestTimer и запускаете его в первый раз через TestTimer::StartOnce, где вы создаете поток (в строке, которая позже выдает исключение).Когда поток завершается, он устанавливает active = false; в timer_func.
Затем вы вызываете TestTimer::StartOnce во второй раз.Поскольку active == false, Stop() не вызывается в текущем потоке, и вы приступаете к созданию нового потока в thread = std::thread(&TestTimer::timer_func, this);.

А потом идет большой , но :
Вы не присоединились к первому потоку до создания второго.И именно поэтому он вызывает исключение.

0 голосов
/ 17 сентября 2018

Если это просто тест

  1. Убедитесь, что обработчик потока пуст или присоединен при вызове деструктора.
  2. Makeвсе, к чему можно получить доступ из многопоточных потоков (в частности, чтение флага active).Просто сделайте это std::atomic_flag.

Кажется, что вы убиваете дескриптор потока, указывающий на активный поток, но трудно сказать, не видя всего приложения.

Если не тест

... тогда, как правило, когда требуется один таймер, повторяющийся или нет, вы можете просто уйти с планирования alarm()сигнал в себя.Вы остаетесь совершенно однопоточным и вам даже не нужно связываться с библиотекой pthread.Пример здесь .

И когда вы ожидаете, что вам понадобится больше таймеров и немного поспите, стоит отбросить экземпляр boost::asio::io_service (или asio::io_service, если вам нужно повысить-бесплатная версия только для заголовков) в ваше приложение, которое имеет зрелую поддержку готовых к работе таймеров.Пример здесь .

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