C++定時器CTimer的實現

W2Y發表於2018-10-26

有過使用Objective-C、Java等語言開發專案的同學會發現,網路或社群有存在眾多的開源庫或元件。個人在使用C++11進行專案開發時,想找一些比較完善、系統化的基本功能庫卻很少,比如定時器。
這裡,主要利用thread、mutex、condition_variable_any、function來實現定時器,可現實同步、非同步、單次、循序執行任務。程式碼中附帶了簡單的註釋,若有疑問或建議歡迎留言。

CTimer.hpp

//
//  CTimer.hpp
//
//  Created by lzj<lizhijian_21@163.com> on 2018/7/20.
//  Copyright © 2018年 ZJ. All rights reserved.
//

#ifndef CTimer_hpp
#define CTimer_hpp

#include <stdio.h>
#include <functional>
#include <chrono>
#include <thread>
#include <atomic>
#include <mutex>
#include <string>
#include <condition_variable>

class CTimer
{
public:
    CTimer(const std::string sTimerName = "");   //構造定時器,附帶名稱
    ~CTimer();
    
    /**
     開始執行定時器

     @param msTime 延遲執行(單位ms)
     @param task 任務函式介面
     @param bLoop 是否迴圈(預設執行1次)
     @param async 是否非同步(預設非同步)
     @return true:已準備執行,否則失敗
     */
    bool Start(unsigned int msTime, std::function<void()> task, bool bLoop = false, bool async = true);
    
    /**
     取消定時器,同步定時器無法取消(若任務程式碼已執行則取消無效)
     */
    void Cancel();
    
    /**
     同步執行一次
     #這個介面感覺作用不大,暫時現實在這裡

     @param msTime 延遲時間(ms)
     @param fun 函式介面或lambda程式碼塊
     @param args 引數
     @return true:已準備執行,否則失敗
     */
    template<typename callable, typename... arguments>
    bool SyncOnce(int msTime, callable&& fun, arguments&&... args) {
        std::function<typename std::result_of<callable(arguments...)>::type()> task(std::bind(std::forward<callable>(fun), std::forward<arguments>(args)...)); //繫結任務函式或lambda成function
        return Start(msTime, task, false, false);
    }
    
    /**
     非同步執行一次任務
     
     @param msTime 延遲及間隔時間
     @param fun 函式介面或lambda程式碼塊
     @param args 引數
     @return true:已準備執行,否則失敗
     */
    template<typename callable, typename... arguments>
    bool AsyncOnce(int msTime, callable&& fun, arguments&&... args) {
        std::function<typename std::result_of<callable(arguments...)>::type()> task(std::bind(std::forward<callable>(fun), std::forward<arguments>(args)...));
        
        return Start(msTime, task, false);
    }
    
    /**
     非同步執行一次任務(預設延遲1毫秒後執行)
     
     @param fun 函式介面或lambda程式碼塊
     @param args 引數
     @return true:已準備執行,否則失敗
     */
    template<typename callable, typename... arguments>
    bool AsyncOnce(callable&& fun, arguments&&... args) {
        std::function<typename std::result_of<callable(arguments...)>::type()> task(std::bind(std::forward<callable>(fun), std::forward<arguments>(args)...));
        
        return Start(1, task, false);
    }
    
    
    /**
     非同步迴圈執行任務

     @param msTime 延遲及間隔時間
     @param fun 函式介面或lambda程式碼塊
     @param args 引數
     @return true:已準備執行,否則失敗
     */
    template<typename callable, typename... arguments>
    bool AsyncLoop(int msTime, callable&& fun, arguments&&... args) {
        std::function<typename std::result_of<callable(arguments...)>::type()> task(std::bind(std::forward<callable>(fun), std::forward<arguments>(args)...));
        
        return Start(msTime, task, true);
    }
    
    
private:
    void DeleteThread();    //刪除任務執行緒

public:
    int m_nCount = 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_hpp */

CTimer.cpp

//
//  CTimer.cpp
//
//  Created by lzj<lizhijian_21@163.com> on 2018/7/20.
//  Copyright © 2018年 ZJ. All rights reserved.
//

#include "CTimer.hpp"
#include <future>
#include "CPrintfLog.h"

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<void()> task, bool bLoop, bool async)
{
    if (!m_bExpired || m_bTryExpired) return false;  //任務未過期(即內部仍在存在或正在執行任務)
    m_bExpired = false;
    m_bLoop = bLoop;
    m_nCount = 0;

    if (async) {
        DeleteThread();
        m_Thread = new std::thread([this, msTime, task]() {
            if (!m_sName.empty()) {
#if (defined(__ANDROID__) || defined(ANDROID))      //相容Android
                pthread_setname_np(pthread_self(), m_sName.c_str());
#elif defined(__APPLE__)                            //相容蘋果系統
                pthread_setname_np(m_sName.c_str());    //設定執行緒(定時器)名稱
#endif
            }
            
            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;  //為了下次再次裝載任務
        });
    } else {
        std::this_thread::sleep_for(std::chrono::milliseconds(msTime));
        if (!m_bTryExpired) {
            task();
        }
        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;
    }
}

用法1:

	CTimer *pTimer = new CTimer("定時器1");
	pTimer->AsyncOnce(10, [](void *userData) {		//延遲10毫秒執行1次
		JMCPCenterCtl *pCenterCtl = static_cast<JMCPCenterCtl*>(userData);    //JMCPCenterCtl是當前類
		if (pCenterCtl) {
	 	CPrintf("這是一個定時器");
	 	}
	}, this);

用法2:

	void JMCPCenterCtl::didHeartbeatThread(void *arg);		//定時器任務函式
    
	CTimer *pTimer = new CTimer("定時器2");
	pTimer->AsyncLoop(10, didHeartbeatThread, this);	//非同步迴圈執行,間隔時間10毫秒

用法3:

	CTimer *pTimer = new CTimer("定時器3");
	pTimer->AsyncLoop(10, [](JMCPCenterCtl *self) {
		printf(“這是一個10毫秒觸發的定時器”);
	}, this);	//非同步迴圈執行,間隔時間10毫秒

用法4:

    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);    //非同步迴圈執行,間隔時間1秒

列印:
	累加 sum += a + b + *sum: 3
	累加 sum += a + b + *sum: 6
	累加 sum += a + b + *sum: 9
	累加 sum += a + b + *sum: 12

相關文章