You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

创建异步Timer类后CreateTimer函数无法返回问题排查

异步Timer类卡住问题排查与修复

我实现了一个可后台运行、自带独立线程的异步Timer类,通过CreateTimer和Delete_Timer函数管理Timer对象,意图支持多线程调用。但运行代码时,CreateTimer函数在打印“returning....”后卡住无法返回,无法排查原因。

原代码如下:

#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() = 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;
}

问题根源

  1. 拷贝构造函数的致命缺陷:std::thread是不可拷贝的对象,但你手动实现的拷贝构造函数完全忽略了thread_成员,只复制了基础字段。当CreateTimer里的Timer c被拷贝进tmrclass时,拷贝出的新Timer对象的thread_是空的,而你调用的是c.start()——也就是原对象的线程。当CreateTimer函数执行完毕,局部变量c被销毁,触发~Timer(),里面的thread_.join()会等待这个正在运行的线程结束,这就是程序卡住的原因。
  2. vector存储方式错误:直接存储Timer对象会触发拷贝操作,而你的Timer类设计没有考虑线程的所有权转移,导致线程管理混乱。

修复方案

关键修改点

  • 禁用拷贝操作:删除拷贝构造和拷贝赋值运算符,因为std::thread不可拷贝,避免意外拷贝导致的未定义行为。
  • 改用智能指针存储:用std::vector<std::unique_ptr<Timer>>替代std::vector<Timer>,通过智能指针管理Timer对象的所有权,避免拷贝。
  • 调整线程启动时机:先启动线程再将智能指针放入vector,确保线程所有权正确转移。
  • 优化析构和停止逻辑:在析构函数中先调用stop(),确保线程被正确唤醒后再join,避免不必要的阻塞。

修复后的完整代码

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

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) = delete;
    Timer& operator=(const Timer&) = delete;

    // 默认移动构造和移动赋值
    Timer(Timer&&) = default;
    Timer& operator=(Timer&&) = default;

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

    void start()
    {
        std::cout<<"starting the timer\n";
        // 确保线程未重复启动
        if (!thread_.joinable())
        {
            thread_ = std::thread{ [this]() 
            {
                std::unique_lock<std::mutex> lck(mutex_);
                cv_.wait_for(lck, msPeriod_, [this]() { return stop_waiting_.load(); });
                if (!stop_waiting_)
                    func_(timerId_, pipeId_);
            }};
        }
    }

    void stop()
    {
        stop_waiting_.store(true);
        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<std::unique_ptr<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);

    auto timer_ptr = std::make_unique<Timer>(timerId, pipeId, msPeriod);
    timer_ptr->start();
    tmrclass.emplace_back(std::move(timer_ptr));

    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);

    // 用find_if高效查找目标Timer
    auto it = std::find_if(tmrclass.begin(), tmrclass.end(), 
        [timerId](const std::unique_ptr<Timer>& t) {
            return t->GetTimerID() == timerId;
        });

    if (it != tmrclass.end())
    {
        std::cout<<"found the timer to delete\n";
        (*it)->stop();
        tmrclass.erase(it); // 移除后智能指针自动释放对象
        ret = 0;
    }

    return ret;
}

int main(int argc, const char *argv[]) 
{
    THRD_EVENTID_T 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 = THRD_STATEB;
    int32_t pipeId2 = 1;
    uint32_t msPeriod2 = 1000;
    CreateTimer(timerId2, pipeId2, msPeriod2);

    Delete_Timer(timerId);

    return 0;
}

修复后说明

  • 程序不再卡住,CreateTimer可以正常返回,因为局部变量不再持有运行中的线程,线程的所有权由vector中的智能指针管理。
  • Delete_Timer现在会直接移除并销毁目标Timer对象,确保资源正确释放。
  • 禁用拷贝操作后,避免了所有因线程拷贝导致的潜在问题。

内容的提问来源于stack exchange,提问作者newww

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 09:29:57