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

posted @ 2019-01-05 21:58  MrYun  阅读(15247)  评论(0编辑  收藏  举报