如何使用C++11实现跨平台的定时器timer

如何使用C++11实现跨平台的定时器timer,第1张

一个Timer的实现需要具备以下几个行为:

StartTimer(Interval, ExpiryAction)

注册一个时间间隔为 Interval 后执行 ExpiryAction 的定时器实例,其中,返回 TimerId 以区分在定时器系统中的其他定时器实例。

StopTimer(TimerId)

根据 TimerId 找到注册的定时器实例并执行 Stop 。

PerTickBookkeeping()

在一个 Tick 时间粒度内,定时器系统需要执行的动作,它最主要的行为,就是检查定时器系统中,是否有定时器实例已经到期。

具体的代码实现思路就是:

StartTimer的时候,把 当前时间 + Interval

作为key放入一个容器,然后在Loop的每次Tick里,从容器里面选出一个最小的key与当前时间比较,如果key小于当前时间,则这个key代表的

timer就是expired,需要执行它的ExpiryAction(一般为回调)。

这里有两个实现的细节:

获取当前时间

包含时间精度,使用系统时间还是CPU时间(asio里的deadline_timer和steady_timer的区别)

常用的API是:

Windows: QueryPerformanceFrequency() 和 QueryPerformanceCounter()

Linux: clock_gettime()

OSX: gettimeofday()或者mach_absolute_time()

当然在C++11里也可以偷懒使用chrono的high_resolution_clock std::chrono::high_resolution_clock

2.timer容器的选择

容器应该能够在很短的时间内找到MinValue

最小堆的find-min复杂度是O(1),所以蛮受人喜欢的

STL里提供有堆的API,make_heap, push_heap, pop_heap, sort_heap

3. PerTickBookkeeping是放在主循环线程还是另起线程

另起线程需要做好线程间通信,asio和skynet有单独的timer线程

一些代码实现:

这是boost.asio的实现的timer_queue,用的是最小堆

asio/timer_queue.hpp at master · chriskohlhoff/asio · GitHub

这是libuv的timer,采用的是红黑树实现(windows),linux下还是最小堆

libuv/timer.c at v1.x · libuv/libuv · GitHub

这是云风的skynet timer实现,采用链表实现

skynet/skynet_timer.c at master · cloudwu/skynet · GitHub

使用进程阻塞函数(休眠函数)就可以。

C++11以上可以用官方函数(单位是s):

std::this_thread::sleep_for(std::chrono::seconds(20))

使用这两个函数还要带上头文件

#include <thread>

#include <chrono>

也可以采用各个平台定义的方法。

例如Windows(单位是ms):

Sleep(20000)

头文件是

#include "windows.h"

其它平台网上可以找到

既然题主没有说要求用什么语言,那我就用c++11实现了。

#include <iostream>

#include <random>

#include <thread>

#include <chrono>

#include <algorithm>

#include <iomanip>

using namespace std

const int size = 10000

float table[size]

int main(){

random_device engine

uniform_real_distribution<float> dist(0, 1)

float sum

for(auto& i: table){

i = dist(engine)

}

auto t_start = chrono::system_clock::now()

sum = accumulate(table, table + size, 0.0)

auto t_end = chrono::system_clock::now()

auto duration = std::chrono::duration_cast<std::chrono::microseconds>(t_end - t_start).count()

cout << "sum of the main thread: " << fixed << setprecision(4) << sum << endl

cout << "time elapsed: " << duration << " micro seconds" << endl

float sum_child[4]

auto fun = [&](int index){

sum_child[index] = accumulate(table + index * size / 4, table + (index + 1) * size / 4, 0.0)

}

t_start = chrono::system_clock::now()

thread thrd_table[4] = {

thread(fun, 0), thread(fun, 1), thread(fun, 2), thread(fun, 3)

}

for(auto& thrd: thrd_table){

thrd.join()

}

sum = 0

sum = accumulate(sum_child, sum_child + 4, 0.0)

t_end = chrono::system_clock::now()

duration = std::chrono::duration_cast<std::chrono::microseconds>(t_end - t_start).count()

cout << "sum of child threads: " << fixed << setprecision(4) << sum << endl

cout << "time elapsed: " << duration << " micro seconds" << endl

return 0

}

编译:

g++ -std=c++11 test.cc -lpthread -o test

运行:

./test

结果:

sum of the main thread: 4976.8721

time elapsed: 0 ms

sum of child threads: 4976.8721

time elapsed: 0 ms

由于随机性每次加和的数值不同,但是精确到毫秒时,时间测出来妥妥的都是零。就是数据量太小,实际运行时间在微秒量级,当然看不出来。

精度改为微秒以后:

sum of the main thread: 4957.9878

time elapsed: 113 micro seconds

sum of child threads: 4957.9878

time elapsed: 560 micro seconds

多线程反而比单线程慢,因为启动线程本身也需要时间。

数据量再增大1000倍:

sum of the main thread: 4999892.0000

time elapsed: 25313 micro seconds

sum of child threads: 4999892.0000

time elapsed: 8986 micro seconds

这回看着正常多了吧


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/7875466.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-10
下一篇 2023-04-10

发表评论

登录后才能评论

评论列表(0条)

保存