定时器模块

类简介

  • 定时器包含两个类Timer和TimerManager,其中Timer类是内部类,不对外开放,外部直接使用TimerManager类。
  • TimerManager类用小根堆管理Timer,根据超时时间排序。
  • TimerManager的关键函数是addTimer、delTimer、handleExpireTimers和getNextExpireTime。

核心代码

1.添加定时器

void TimerManager::addTimer(HttpRequest* request, 
                     const int& timeout, 
                     const TimeoutCallBack& cb)
{
	//对作用域内的全部数据加锁
    std::unique_lock<std::mutex> lock(lock_);
    assert(request != nullptr);

    updateTime();
    //新的计时器
    Timer* timer = new Timer(now_ + MS(timeout), cb);
    timerQueue_.push(timer);

    // 对同一个request连续调用两次addTimer,需要把前一个定时器删除
    if(request -> getTimer() != nullptr)
        delTimer(request);

    request -> setTimer(timer);
}

2.删除定时器

void TimerManager::delTimer(HttpRequest* request)
{
	//这个函数不必上锁,没有线程安全问题
	//若上锁,会因连续两次上锁造成死锁:handleExpireTimers->runCallBack->__closeConnection->delTimer
    //std::unique_lock<std::mutex> lock(lock_);
    assert(request != nullptr);

    Timer* timer = request -> getTimer();
    if(timer == nullptr)
        return;

    //如果这里写成delete timeNode,会使priority_queue里的对应指针变成垂悬指针
    //正确的方法是惰性删除
    timer -> del();
    //防止request -> getTimer()访问到垂悬指针
    request -> setTimer(nullptr);
}

3.调用超时定时器的超时回调函数

void TimerManager::handleExpireTimers()
{
    std::unique_lock<std::mutex> lock(lock_);
    //更新时间为当前时间
    updateTime();
    while(!timerQueue_.empty()) {
        Timer* timer = timerQueue_.top();
        assert(timer != nullptr);
        // 定时器被删除
        if(timer -> isDeleted()) { //定时器删除标识为真
            // std::cout << "[TimerManager::handleExpireTimers] timer = " << Clock::to_time_t(timer -> getExpireTime())
            //           << " is deleted" << std::endl;
            timerQueue_.pop(); //出队
            delete timer; //删除定时器
            continue;
        }
        //优先队列头部的定时器也没有超时,return
        if(std::chrono::duration_cast<MS>(timer -> getExpireTime() - now_).count() > 0) {
            // std::cout << "[TimerManager::handleExpireTimers] there is no timeout timer" << std::endl;
            return;
        }
        // std::cout << "[TimerManager::handleExpireTimers] timeout" << std::endl;
        //定时器超时
        timer -> runCallBack();
        timerQueue_.pop();
        //调用超时回调函数
        delete timer;
    }
}

4.获取最近的超时时间

int TimerManager::getNextExpireTime()
{
    std::unique_lock<std::mutex> lock(lock_);
    updateTime();
    int res = -1;
    while(!timerQueue_.empty()) {
        Timer* timer = timerQueue_.top();
        if(timer -> isDeleted()) {
            timerQueue_.pop();
            delete timer;
            continue;
        }
        res = std::chrono::duration_cast<MS>(timer -> getExpireTime() - now_).count();
        res = (res < 0) ? 0 : res;
        break;
    }
    return res;
}

问题

1.为什么需要加锁?不加锁会有哪些安全问题?

2.为什么删除定时器函数不加锁?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值