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


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

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

    #include 
    #include 
    #include 
    #include 
    #include 
    
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

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

    
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    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_;
    };
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85

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

    static TimerWheel timer(10, 1000);
    
    • 1

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

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

    可以在需要的时候停止

    timer.Stop();
    
    • 1

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

  • 相关阅读:
    Python循环结构
    手写一个民用Tomcat (05)
    leetcode刷题日记:94. Binary Tree Inorder Traversal(二叉树的中序遍历)
    漏洞(某渗透测试)修复-springboot项目使用内置tomcat去除\隐藏页面的异常报错信息以及版本号信息,亲测有效。
    Kafka ProducerConfig和ConsumerConfig配置
    【Java进阶】学好常用类,code省时省力(一)
    【BUG记录】Python中的相对文件路径
    CAD Exchanger SDK 3.22.0 Crack
    一种单键开关机电路图
    Java之接口和抽象类详解
  • 原文地址:https://blog.csdn.net/winnyrain/article/details/134049574