C++ 实现定时器的两种方法(线程定时和时间轮算法修改版)

定时器要求在固定的时间异步执行一个操作,比如boost库中的boost::asio::deadline_timer,以及MFC中的定时器。也可以利用c++11的thread, mutex, condition_variable 来实现一个定时器。

1、使用C++11中的thread, mutex, condition_variable来实现一个定时器。
注:此算法会每一个任务创建一个线程,不推荐。推荐用最下面第2种时间轮算法

#include <iostream>
#include <chrono>
#include <thread>
#include <mutex>
#include <condition_variable>

class Timer {
public:
    Timer() :_expired(true), _try_to_expire(false) {}

    Timer(const Timer& t) {
        _expired = t._expired.load();
        _try_to_expire = t._try_to_expire.load();
    }

    ~Timer() {
        Expire();
    }

    void StartTimer(int interval, std::function<void()> task) {
        if (_expired == false) {
            return;
        }
        _expired = false;
        std::thread([this, interval, task]() {
            while (!_try_to_expire) {
                std::this_thread::sleep_for(std::chrono::milliseconds(interval));
                task();
            }
            {
                std::lock_guard<std::mutex> locker(_mutex);
                _expired = true;
                _expired_cond.notify_one();
            }
        }).detach();
    }

    void Expire() {
        if (_expired) {
            return;
        }

        if (_try_to_expire) {
            return;
        }
        _try_to_expire = true;
        {
            std::unique_lock<std::mutex> locker(_mutex);
            _expired_cond.wait(locker, [this] {return _expired == true; });
            if (_expired == true) {
                _try_to_expire = false;
            }
        }
    }

private:
    std::atomic<bool> _expired;
    std::atomic<bool> _try_to_expire;
    std::mutex _mutex;
    std::condition_variable _expired_cond;
};

int main() {
    Timer t;
    t.StartTimer(1000, []() {std::cout << "Hello World!" << std::endl; });
    std::this_thread::sleep_for(std::chrono::seconds(4));
    t.Expire();
    return 0;
}

2、使用时间轮算法:Linux内核就有这个算法。这里也有一个用户态的实现供参考:github.com/facebook/folly。它的高精度版本能实现微妙级别的定时。下面是一个简单的时间轮定时器的C++实现。原文的代码有问题,不能循环定时,经修改已经支持:


#include <chrono>
#include <functional>
#include <list>
#include <mutex>
#include <thread>
#include <vector>

class TimerWheel {
public:
    using Task = std::function<void()>;

    explicit TimerWheel(size_t wheel_size, int interval_ms)
        : wheel_size_(wheel_size),
        interval_ms_(interval_ms),
        wheel_(wheel_size),
        current_index_(0) {}

    ~TimerWheel() {
        Stop();
    }

    void Start() {
        if (running_) {
            return;
        }
        running_ = true;
        thread_ = std::thread([this]() {
            while (running_) {
                std::this_thread::sleep_for(std::chrono::milliseconds(interval_ms_));
                Tick();
            }
        std::cout << "timer oooops!" << std::endl;
            });
        thread_.detach();
    }

    void Stop() {
        if (!running_) {
            return;
        }
        running_ = false;
        if (thread_.joinable()) {
            thread_.join();
        }
    }

    void AddTask(int timeout_ms, Task task) {
        std::lock_guard<std::mutex> lock(mutex_);
        size_t ticks = timeout_ms / interval_ms_;
        size_t index = (current_index_ + ticks) % wheel_size_;
        size_t allindex = index;
        for (size_t i = 1 ; allindex < wheel_size_; i++)
        {
            allindex = index * i;
            if (allindex >= wheel_size_)
                break;
            wheel_[allindex].push_back(task);
        }
        
    }

private:
    void Tick() {
        std::lock_guard<std::mutex> lock(mutex_);
        auto& tasks = wheel_[current_index_];
        for (const auto& task : tasks) {
            task();
        }
        //tasks.clear();
        current_index_ = (current_index_ + 1) % wheel_size_;
    }

private:
    size_t wheel_size_;
    int interval_ms_;
    std::vector<std::list<Task>> wheel_;
    size_t current_index_;
    bool running_ = false;
    std::thread thread_;
    std::mutex mutex_;
};



使用方法:
使用static声明以免被析构,可在cpp类外全局声明,第一个参数为任务容器最大数量,第二个参数为定时判断的毫秒数即最低检测时间单位

static TimerWheel timer(10, 1000);

在要使用的地方,启动并添加任务

timer.Start();
timer.AddTask(2000, []() {std::cout << "Task 1" << std::endl; });
timer.AddTask(3000, []() {std::cout << "Task 2" << std::endl; });

可以在需要的时候停止

timer.Stop();

原文链接:https://blog.csdn.net/sinat_28305511/article/details/131495316

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
乘风的头像乘风管理团队
上一篇 2023年12月15日
下一篇 2023年12月15日

相关推荐