C++ Timer


Timer機制

這里所說的Timer機制是定時器(Timer),例如在Javascript中就提供定時執行代碼的功能。但是在C++標准中暫時沒有實現這一功能的函數。

Javascript中的Timer

Javascript用來處理延時和定時任務的setTimeOut和setInterval函數應用非常廣泛,它們都用來處理延時和定時任務,比如打開網頁一段時間后彈出一個登錄框,頁面每隔一段時間發送異步請求獲取最新數據等等。但它們的應用是有區別的。

setTimeout

setTimeout函數用來指定某個函數或某段代碼,在多少毫秒之后執行。它返回一個整數,表示定時器的編號,以后可以用clearInterval來取消這個定時器。setTimeout只執行一次。

var timeId=setTimeout(func|code,delay)    //間隔delay毫秒執行函數func或者code

比如下面這段代碼:

console.log('First');
setTimeout('console.log('Second')',1000);
console.log('Third');

的執行結果是先打印出"First與Third",然后再1000ms之后打印出"Second"。

而在傳入函數的時候,我們還可以增加參數:

setTimeout(function(a,b,c){console.log(a+b);console.log(c);},1000,1,1,5);

此外,如果被setTimeout推遲執行的回調函數是某個對象的方法,那么用到的this關鍵字將指向全局環境:

var x = 1;var o = {x: 2,y: function(){console.log(this.x);}};setTimeout(o.y,1000);

執行結果x的值為1而不是定義的對象o中的2!

給出一個實際中使用的小例子:

function User(login) {
    this.login = login;
    this.sayHi = function() 
    {console.log("Hello "+this.login);}
}
var user = new User('John');
setTimeout(function() {user.sayHi();}, 1000);

setTimeout這里調用的為function(){user.sayHi()};,因為如果執行user.sayHi,他會再全局對象中執行,this.login取不到值。

setInterval

setInterval函數的用法與setTimeout完全一致,區別僅僅在於setInterval指定某個任務每隔一段時間就執行一次,也就是無限次的定時執行。對應的取消函數為clearInterval

運行機制

setTimeout和setInterval的運行機制是,將指定的代碼移出本次執行,等到下一輪Event Loop時,再檢查是否到了指定時間。如果到了,就執行對應的代碼;如果不到,就等到再下一輪Event Loop時重新判斷。這意味着,setTimeout指定的代碼,必須等到本次執行的所有代碼都執行完,才會執行。
每一輪Event Loop時,都會將“任務隊列”中需要執行的任務,一次執行完。setTimeout和setInterval都是把任務添加到“任務隊列”的尾部。因此,它們實際上要等到當前腳本的所有同步任務執行完,然后再等到本次Event Loop的“任務隊列”的所有任務執行完,才會開始執行。由於前面的任務到底需要多少時間執行完,是不確定的,所以沒有辦法保證,setTimeout和setInterval指定的任務,一定會按照預定時間執行。
比如說我用了setTimeout延遲了一個函數1000ms后執行,但是我代碼后面存在耗時超過1000ms的執行代碼,那么我利用setTimeout延遲的函數必須等到耗時很長的代碼執行完成后才能輪到它執行。這一點會造成一種"累積觸發"的現象,假如是使用setInterval,存在上述耗時很長的代碼,那么setInterval的函數會排隊並逐漸被累積起來,等到前面的代碼執行完成會一股腦的連續觸發。

C++ Timer

一個非常簡單的庫

這個是由shalithasuranga寫的非常簡易的timer
我們將代碼貼在下面:

main.cpp:

#include <iostream>
#include "timercpp.h"

using namespace std;

int main() {
    Timer t = Timer();

    t.setInterval([&]() {
        cout << "Hey.. After each 1s..." << endl;
    }, 1000); 

    t.setTimeout([&]() {
        cout << "Hey.. After 5.2s. But I will stop the timer!" << endl;
        t.stop();
    }, 5200); 

    cout << "I am Timer" <<endl;
    while(true); // Keep mail thread active
}

timercpp.h:

#include <iostream>
#include <thread>
#include <chrono>

class Timer {
    bool clear = false;

    public:
        void setTimeout(auto function, int delay);
        void setInterval(auto function, int interval);
        void stop();

};

void Timer::setTimeout(auto function, int delay) {
    this->clear = false;
    std::thread t([=]() {
        if(this->clear) return;
        std::this_thread::sleep_for(std::chrono::milliseconds(delay));
        if(this->clear) return;
        function();
    });
    t.detach();
}

void Timer::setInterval(auto function, int interval) {
    this->clear = false;
    std::thread t([=]() {
        while(true) {
            if(this->clear) return;
            std::this_thread::sleep_for(std::chrono::milliseconds(interval));
            if(this->clear) return;
            function();
        }
    });
    t.detach();
}

void Timer::stop() {
    this->clear = true;
}

首先看一下main函數,調用了兩個接口setTimeout與setInterval函數,兩個函數是lambda表達式(捕捉引用符號)。其實就是一個簡單的測試接口函數。

下面再談一談Timer的接口,首先auto的寫法適用於C++20,為了符合眾多編譯器,我們可以改成模板形式:

#include <iostream>
#include <thread>
#include <chrono>

class Timer {
	bool clear = false;

public:
	template<typename T>
	void setTimeout(T function, int delay);
	template<typename T>
	void setInterval(T function, int interval);
	void stop();

};

template<typename T>
void Timer::setTimeout(T function, int delay) {
	this->clear = false;
	std::thread t([=]() {
		if (this->clear) return;
		std::this_thread::sleep_for(std::chrono::milliseconds(delay));
		if (this->clear) return;
		function();
	});
	t.detach();
}

template<typename T>
void Timer::setInterval(T function, int interval) {
	this->clear = false;
	std::thread t([=]() {
		while (true) {
			if (this->clear) return;
			std::this_thread::sleep_for(std::chrono::milliseconds(interval));
			if (this->clear) return;
			function();
		}
	});
	t.detach();
}

void Timer::stop() {
	this->clear = true;
}

在Timer類中定義了一個clear變量,是用來表示計時器是否應該停止的變量。然后出現了std::thread t([=]),這個lambda函數用來判斷計時器是否應該停止,如果停止了就立刻返回。然后調用了std::this_thread::sleep_for用來休眠線程,可以參看這個.功能為阻塞當前線程執行,至少經過指定的 sleep_duration 。此函數可能阻塞長於 sleep_duration ,因為調度或資源爭議延遲。

我們分離:t.detach(),從 thread 對象分離執行的線程,允許執行獨立地持續。一旦線程退出,則釋放所有分配的資源。

Boost Asio

再有就是Boost庫了,但是這個庫過於龐大,有興趣的可以去看一看:Boost Asio


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM