创建异步计时器的新类后函数未返回

function is not returning after creating a new class of an asynchronous timer

提问人:newww 提问时间:10/16/2023 更新时间:10/16/2023 访问量:72

问:

我有一个类正在创建一个应该在后台运行的异步计时器。我创建了一个计时器类,该类使用其单独的线程创建一个对象,并且应该在后台运行。最重要的是,我有两个函数 CreateTimer 和 DeleteTimer,它们将用于访问计时器类对象。我的目的是为多个线程调用 CreateTimer 和 DeleteTimer。我有以下代码:

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



typedef struct
{
    uint32_t  msgId;
    uint32_t  param1;
    uint32_t  param2;
}THRD_EVENT_T;


enum THRD_EVENTID_T
{
    THRD_MSGID_TIMER_FIRST,

    THRD_STATEA,
    THRD_STATEB,
    THRD_STATEC,

    THRD_MSGID_TIMER_LAST
};

std::mutex m;





void func_(THRD_EVENTID_T timerId_, int32_t pipeId_ )
{

    std::cout<<"done\n";
}


class Timer 
{

public:
    Timer(THRD_EVENTID_T timerId, int32_t pipeId, uint32_t msPeriod)
        : timerId_(timerId)
        , pipeId_(pipeId)
        , msPeriod_(msPeriod)
    {

    }


    Timer(const Timer &obj) // copy constructor
    {
        timerId_ = obj.timerId_;
        pipeId_ = obj.pipeId_;
        msPeriod_ = obj.msPeriod_; 
    }
    // Timer()
    // {

    // }

    Timer() = delete;
    Timer(Timer&&) = default;


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

    void start()
    {
        std::cout<<"starting the timer\n";
        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_)
                func_(timerId_, pipeId_);
            }
        };
    }

    void stop()
    {
        stop_waiting_.store(true);
        std::cout<<"Stopping the timer\n";
        cv_.notify_one();
    }

    THRD_EVENTID_T GetTimerID()
    {
        return timerId_;
    }

private:
    std::chrono::milliseconds msPeriod_;
    THRD_EVENTID_T timerId_;
    int32_t pipeId_;
    std::thread thread_;
    std::condition_variable cv_;
    std::mutex mutex_;
    std::atomic<bool> stop_waiting_{false};
};


std::vector<Timer> tmrclass;

int32_t CreateTimer( const THRD_EVENTID_T timerId, const int32_t pipeId, const uint32_t msPeriod )
{
    std::cout<<"Creating the timer\n";
    std::unique_lock<std::mutex> lock(m);

    Timer c (timerId, pipeId, msPeriod);

    tmrclass.emplace_back(c);

    c.start();

    std::cout<<"exiting the createtimer function\n";

    std::cout<<"returning....\n";
    return 0;

}


int32_t Delete_Timer( const THRD_EVENTID_T timerId)
{
    int32_t ret = -1;
    std::cout<<"Deleting the timer function\n";
    std::unique_lock<std::mutex> lock(m);

    for (auto &f : tmrclass)
    {
        if(f.GetTimerID() == timerId)
        {
            std::cout<<"found the timer to delete\n";
            f.stop();
            break;
        }
        else
        {
            ret = -1;
        }

    }

     return ret;
}


int main(int argc, const char *argv[]) 
{

    THRD_EVENTID_T timerId;
    timerId  =  THRD_STATEA;
    int32_t pipeId = 0;
    uint32_t msPeriod = 5000;

    CreateTimer(timerId, pipeId, msPeriod);
    std::cout<<"Have I exited the resource\n";

    THRD_EVENTID_T timerId2;
    timerId2  =  THRD_STATEB;
    int32_t pipeId2 = 1;
    uint32_t msPeriod2 = 1000;
    CreateTimer(timerId2, pipeId2, msPeriod2);

    Delete_Timer(timerId);


    return 0;
}

当我运行它时,代码没有从函数返回,它最终在打印出来后被卡住了,我无法弄清楚为什么它没有从函数返回CreateTimerreturning....CreateTimer

C++ 多线程 死锁

评论

0赞 Botje 10/16/2023
因为 ur 是一个局部变量,并且你的析构函数很可能在它上面。这将是打破调试器并检查处于“挂起”状态的实际系统而不是依赖输出和猜测的好时机。Timer cjoin()
1赞 user12002570 10/16/2023
什么是调试器,它如何帮助我诊断问题?
1赞 JaMiT 10/16/2023
调试器是一个很好的建议。如果出于某种原因不想使用诊断输出,请添加更多诊断输出。当您感到卡住时,请在每行代码后添加诊断(当然,其他诊断除外)。如果将代码简化为最小的可重现示例,则这将变得更容易 - 例如,删除重现问题不需要的所有数据成员和参数。特别是,可以消除(因为它永远不会达到),就像用于查找要删除的计时器的所有参数一样。 当您的目标是重现错误时,可能没有参数。Delete_TimerCreateTimer

答:

2赞 Some programmer dude 10/16/2023 #1

这是因为对象被破坏,它试图加入线程。它将阻塞,直到线程完成。这会导致死锁,因为没有人告诉线程退出。c

对于真正独立的计时器,您需要分离线程。或者你需要使用一些其他的设计(单线程事件循环是常用的)。或者改用操作系统原生计时器。

评论

0赞 newww 10/16/2023
我究竟如何使用分离?在线程分离后,我是否需要跟踪线程?
1赞 Öö Tiib 10/16/2023
@newww问题是,您的“创建计时器”不仅会创建一个计时器,还会立即销毁它。这也许是你不希望它做的事情。在分离的被破坏对象上运行线程操作当然是非常糟糕的主意。
0赞 Some programmer dude 10/16/2023
@newww 这实际上比我最初想象的还要糟糕......在将对象添加到向量时,可以创建对象的副本,但保留对象中线程的所有权。线程不能被复制,只能被移动。您需要更多地考虑所有权,并将对象移动到向量所有。Timerccc
3赞 Some programmer dude 10/16/2023
@newww 您可以直接在矢量内创建计时器对象,而不是将计时器对象的所有权转让给向量。而不是你可以做.那么你根本不需要这个对象。我还建议您将复制构造函数标记为已删除,因为无法真正复制对象。tmrclass.emplace_back(c);tmrclass.emplace_back(timerId, pipeId, msPeriod);cTimer