C++ 程序设计:基于线程的定时器

1.前言

在C++11之前,标准库中没有提供原生的定时器功能

2.定时器启动实现原理

启动定时器任务时去创建一个新的线程,并在指定的时间间隔后执行传入的任务函数 task。可以根据参数来控制是否循环执行任务。任务执行的过程中,通过条件变量和互斥锁来控制线程的等待和唤醒。

3.实现

#ifndef CTIMER_H
#define CTIMER_H

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

class CTimer
{
public:
    CTimer(const std::string sTimerName = "");   //构造定时器,附带名称
    ~CTimer();
    /**
      开始运行定时器

      @param msTime 延迟运行(单位ms)
      @param task 任务函数接口
      @param bLoop 是否循环(默认执行1次)
      @return true:已准备执行,否则失败
    */
    bool Start(unsigned int msTime, std::function task, bool bLoop = false);

    /**
       取消定时器,同步定时器无法取消(若任务代码已执行则取消无效)
    */
    void Cancel();

    /**
       异步执行一次任务

        @param msTime 延迟及间隔时间(毫秒)
        @param fun 函数接口或lambda代码块
        @param args 参数
        @return true:已准备执行,否则失败
    */
    template
    bool AsyncOnce(int msTime, callable&& fun, arguments&&... args) {
        std::function::type()> task(std::bind(std::forward(fun), std::forward(args)...));

        return Start(msTime, task, false);
    }

    /**
         异步循环执行任务

         @param msTime 延迟及间隔时间(毫秒)
         @param fun 函数接口或lambda代码块
         @param args 参数
         @return true:已准备执行,否则失败
    */
    template
    bool AsyncLoop(int msTime, callable&& fun, arguments&&... args) {
        std::function::type()> task(std::bind(std::forward(fun), std::forward(args)...));

        return Start(msTime, task, true);
    }

public:
    //获取时间戳(毫秒)
    static uint64_t Timestamp();

    //获取格式化时间
    static std::string FormatTime(const std::string sFormat = "%Y-%m-%d %H:%M:%S");

    //获取UTC时间
    static struct tm *UTCTime(long long secTime = 0);

    //获取UTC时间(秒)
    static int64_t UTCTime();

    //获取与0时区的时差(以秒为单位)
    static int TimeDifFrimGMT();

private:
    void DeleteThread();    //删除任务线程

public:
    int m_nCount = 0;   //循环次数
    int m_nTag = 0;     //定时器标签

private:
    std::string m_sName;   //定时器名称

    std::atomic_bool m_bExpired;       //装载的任务是否已经过期
    std::atomic_bool m_bTryExpired;    //装备让已装载的任务过期(标记)
    std::atomic_bool m_bLoop;          //是否循环

    std::thread *m_Thread = nullptr;
    std::mutex m_ThreadLock;
    std::condition_variable_any m_ThreadCon;
};

#endif // CTIMER_H
#include "ctimer.h"
#include 
#include 
#include 
#include 

CTimer::CTimer(const std::string sTimerName):m_bExpired(true), m_bTryExpired(false), m_bLoop(false)
{
    m_sName = sTimerName;
}

CTimer::~CTimer()
{
    m_bTryExpired = true;   //尝试使任务过期
    DeleteThread();
}

bool CTimer::Start(unsigned int msTime, std::function task, bool bLoop)
{
    if (!m_bExpired || m_bTryExpired) return false;  //任务未过期(即内部仍在存在或正在运行任务)
    m_bExpired = false;
    m_bLoop = bLoop;
    m_nCount = 0;

    DeleteThread();
    m_Thread = new std::thread([this, msTime, task]()
    {
        while (!m_bTryExpired)
        {
            m_ThreadCon.wait_for(m_ThreadLock, std::chrono::milliseconds(msTime));  //休眠
            if (!m_bTryExpired)
            {
                task();     //执行任务

                m_nCount ++;
                if (!m_bLoop)
                {
                    break;
                }
            }
        }

        m_bExpired = true;      //任务执行完成(表示已有任务已过期)
        m_bTryExpired = false;  //为了下次再次装载任务
    });

    return true;
}

void CTimer::Cancel()
{
    if (m_bExpired || m_bTryExpired || !m_Thread)
    {
        return;
    }

    m_bTryExpired = true;
}

void CTimer::DeleteThread()
{
    if (m_Thread)
    {
        m_ThreadCon.notify_all();   //休眠唤醒
        m_Thread->join();           //等待线程退出
        delete m_Thread;
        m_Thread = nullptr;
    }
}

uint64_t CTimer::Timestamp()
{
    uint64_t msTime = 0;

    struct timespec abstime;
    clock_gettime(CLOCK_REALTIME, &abstime);

    msTime = ((uint64_t)abstime.tv_sec) * 1000 + ((uint64_t)abstime.tv_nsec) / 1000000;   //需要强制转long long

    return msTime;
}

std::string CTimer::FormatTime(const std::string sFormat)
{
    time_t timep;
    time (&timep);

    char tmp[64];
    strftime(tmp, sizeof(tmp), sFormat.c_str(), localtime(&timep));

    return std::string(tmp);
}

struct tm *CTimer::UTCTime(long long secTime)
{
    time_t timep;
    if (secTime) {
        timep = secTime;
    } else {
        time (&timep);
    }

    struct tm *data = gmtime(&timep);
    data->tm_year += 1900;
    data->tm_mon += 1;

    return data;
}

int64_t CTimer::UTCTime()
{
    int64_t msTime = 0;

    struct timespec abstime;
    clock_gettime(CLOCK_REALTIME, &abstime);

    msTime = (int64_t)abstime.tv_sec;

    return msTime;
}

int CTimer::TimeDifFrimGMT()
{
    time_t now = time(NULL);
    struct tm *gmTime = gmtime(&now);
    if (gmTime)
    {
        return (int)difftime(now, mktime(gmTime));
    }

    return 0;
}

4.简单使用

int main()
{
    /* 方式1 延迟10毫秒执行1次 */
    CTimer *pTimer = new CTimer("定时器1");
    pTimer->AsyncOnce(10, [](void *userData) //
    {
        CPCenterCtl *pCenterCtl = static_cast(userData);    //CPCenterCtl是当前类
        if (pCenterCtl)
        {
             CPrintf("这是一个定时器");
        }
    }, this);

/* 方式2 异步循环执行,间隔时间10毫秒 */
    void CPCenterCtl::didHeartbeatThread(void *arg);	//定时器任务函数

    CTimer *pTimer = new CTimer("定时器2");
    pTimer->AsyncLoop(10, didHeartbeatThread, this);	

/* 方式3 异步循环执行,间隔时间10毫秒:*/
    CTimer *pTimer = new CTimer("定时器3");
    pTimer->AsyncLoop(10, [](JMCPCenterCtl *self) 
    {
        printf(“这是一个10毫秒触发的定时器”);
    }, this);

/* 方式4 异步循环执行,间隔时间1秒 */
    int a = 1;
    int b = 2;
    int sum = 0;
    CTimer *pTimer = new CTimer("定时器4");
    pTimer->AsyncLoop(1000, [](void *self, int a, int b, int *sum)
    {
        *sum = a + b + *sum;
        printf("累加 sum += a + b + *sum: %d\n", *sum);
    }, this, a, b , &sum);   

/* 打印:
        累加 sum += a + b + *sum: 3
        累加 sum += a + b + *sum: 6
        累加 sum += a + b + *sum: 9
        累加 sum += a + b + *sum: 12  */
}

你可能感兴趣的:(C/C++,设计师,c++)