调用 terminate 后退出 C++ 线程,没有活动异常

C++ thread exits after calling terminate called without an active exception

提问人:newww 提问时间:11/8/2023 最后编辑:newww 更新时间:11/8/2023 访问量:102

问:

我创建了一个用于创建和删除计时器的计时器类,并且我有固定数量的计时器。一些计时器在完成回调功能后会重新启动。在某些情况下,我可能需要删除计时器。有了这个逻辑,我想出了以下代码:

计时器.cpp:

#include <condition_variable>
#include <mutex>
#include <chrono>
#include <vector>
#include <algorithm>
#include <iostream>
#include <thread>
#include <atomic>

class Timer
{
    public:
        Timer();

        ~Timer();

        void Start( const uint8_t pipeId, const uint32_t msPeriod);

        void Stop();

    private:

        void TimerCallbacktimer( const uint8_t pipeId);

        std::chrono::milliseconds msPeriod_;
        uint8_t pipeId_;
        std::thread thread_;
        std::condition_variable cv_;
        std::mutex mutex_;
        std::atomic<bool> stop_waiting_{false};
        std::atomic<bool> done_{false};


};


void Timer::TimerCallbacktimer( const uint8_t pipeId)
{
    //callback function left empty on purpose
}



void Timer::Start( const uint8_t pipeId, const uint32_t msPeriod)
{
    pipeId_ = pipeId;
    msPeriod_ = std::chrono::milliseconds(msPeriod);
    if (done_) //if the thread was already created and expired join it before restarting the thread
    {
        thread_.join();
        done_.store(false);
    }

    thread_ = std::thread
    {
        [this]() 
        {
            std::unique_lock<std::mutex> lck(mutex_);
            cv_.wait_for(lck, msPeriod_, [this]() { return stop_waiting_.load(); });
            if (not stop_waiting_)
            {
                TimerCallbacktimer(pipeId_);
                done_.store(true);
            }
        }
    };
}


void Timer::Stop()
{
    stop_waiting_.store(true);
    cv_.notify_one(); 
}



Timer::Timer()
{

}

Timer::~Timer()
{
    if (thread_.joinable())
    {
        thread_.join();
    }
}



std::vector<Timer> TimerArray(30);

std::mutex timerMutex;

void CreateTimer( const uint8_t slot , const int32_t pipeId, const uint32_t msPeriod )
{
    std::unique_lock<std::mutex> lock(timerMutex);
    TimerArray[slot].Start(pipeId, msPeriod);
}


void DeleteTimer( const uint8_t slot )
{
    std::unique_lock<std::mutex> lock(timerMutex);
    TimerArray[slot].Stop();

}

int main(int argc, char const *argv[])
{
    
    CreateTimer(3, 3, 300);
    CreateTimer(4, 3, 100);


    DeleteTimer(4);

    CreateTimer(3, 3, 100);
    // sometimes I start the same timer immediately after it has called its callback function
    CreateTimer(3, 3, 100); 

    return 0;
}

由于我确实有固定数量的计时器,因此我确实会在有 我面临的问题是有时我会得到一个错误。我似乎无法弄清楚为什么会弹出错误。我脑海中突然出现的东西基本上是我在计时器过期后立即再次启动计时器,我需要在回调函数中进行一些清理以准备它再次启动?另外,我应该在回调函数完成时加入线程吗?terminate called without an active exception

C++ 计时器 线程安全

评论

4赞 Botje 11/8/2023
在调试器下运行代码,它会告诉你为什么调用 terminate。
1赞 463035818_is_not_an_ai 11/8/2023
现在我可以重现崩溃了。请注意,为了 mre,将所有代码放在一个文件中会更简单,就像我在这里所做的那样 godbolt.org/z/a5T9Mn39o
1赞 molbdnilo 11/8/2023
据我所知,您没有处理尝试重新启动尚未完成的线程的情况。为什么只有在超时已经过去的情况下才重新启动?join
0赞 newww 11/8/2023
@molbdnilo。我认为在这一点上加入它会更合适,我正在考虑在回调中调用 join,但当我这样做时它给出了额外的错误
1赞 molbdnilo 11/8/2023
@newww 不,我的意思是,即使你是假的,你也应该这样做。此时线程可能仍在运行。joindone_

答:

1赞 463035818_is_not_an_ai 11/8/2023 #1

正如 molbdnilo 的评论中提到的,当计时器已经在运行但尚未运行时,您不会处理调用的情况。Startdone_

要重新启动计时器,您可以向 发出信号,它(并重置为其初始值):stop_waiting_joindone_

//if (done_) //if the thread was already created and expired join it before restarting the thread

// if the thread was already created, join it!
if (thread_.joinable()){
    stop_waiting_.store(true);
    thread_.join();
    done_.store(false);
}

现场演示

代码中的崩溃是由联接之前导致的。它与这个非常简化的代码存在相同的问题:thread_ = std::thread ...

int main() {
    std::thread t{[](){
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }};
    t = std::thread([](){});   // -> terminates
}

有关详细信息,我建议您访问以下 cppreferencestd::thread::operator=(thread&& other)

如果仍然有一个关联的正在运行的线程(即 ),则调用 .否则,将 的状态分配给 并设置为默认构造状态。*thisjoinable() == truestd::terminate()other*thisother

在此调用之后,等于调用之前的值,并且不再表示执行的线程。this->get_id()other.get_id()other

评论

0赞 newww 11/9/2023
解决方案奏效了,但我不得不将 stop_waiting_.store(true) 更改为 stop_waiting_.store(false)。因为它带来了僵局
0赞 463035818_is_not_an_ai 11/9/2023
@newww嗯......太取消了等待的呼叫。否则必须等待计时器过期。也许我错过了什么,没有看到僵局,但很高兴它有所帮助。stop_waiting_.store(true);wait_forstop_waiting_truethread_.join();