js 的setTimer和setInterval非常好用,於是想到在c++實現一個類似語法簡潔的定時器。
github上已有實現timercpp
其使用方式如下:
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);
非常簡潔。可惜項目使用了auto關鍵字不能用在c++11 的版本上,於是考慮做一些更改,想到我們可以使用模板來實現類似的功能。
完整的實現如下:
(使用方法和上面例子一樣)
#include <iostream>
#include <thread>
#include <chrono>
class Timer {
bool clear = false;
public:
template<typename Function>
void setTimeout(Function function, int delay);
template<typename Function>
void setInterval(Function function, int interval);
void stop();
};
template<typename Function>
void Timer::setTimeout(Function 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 Function>
void Timer::setInterval(Function 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;
}
分析:
接下來我們嘗試分析一下原理:
在 setTimeout中,第一行設置用於控制計時器處於活動狀態還是非活動狀態的標誌,以將其設置爲活動狀態( 不清除該定時器 ):
this->clear = false;
接下來實例化一個新的線程對象,參數是lambda函數,[=]()
是指用到的外部元素全部按值操作。
std::thread t([=]() {
該(lambda)函數首先檢查timer是否仍處於活動狀態(否則它將立即返回),因爲它可能已被另一個函數停止。如果處於活動狀態,它將等待,sleep_for 會阻塞線程指定的時間:
if(this->clear) return;
std::this_thread::sleep_for(std::chrono::milliseconds(delay));
然後,我們再次檢查計時器是否仍處於活動狀態,如果是,則調用傳遞給setTimeout的函數:
if(this->clear) return;
function();
但是這個線程對象的壽命很短:它將在setTimeout函數結束時被銷燬。而且我們希望真實線程的壽命超過線程對象的壽命。當thread::detach()函數被調用後,執行的線程從線程對象中被分離,已不再被一個線程對象所表達--這是兩個獨立的事情。C++線程對象可以被銷燬,同時OS執行的線程可以繼續。如果程序想要知道執行的線程何時結束,就需要一些其它的機制。