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
