目录
例如:nginx、redis、memcached;
定时器通常是与网络组件一起工作,⽹络事件和时间事件在⼀个线程当中配合使⽤;例如nginx、redis,我们将epoll_wait的第四个参数timeout设置为最近要触发的定时器的时间差来触发定时器,来执行任务。
- // 网络事件和定时事件在一个线程中处理
- while (!quit) {
- int timeout = get_nearest_timer() - now();
- if (timeout < 0) timeout = -1;
- int nevent = epoll_wait(epfd, ev, nev,timeout);
- for (int i=0; i<nevent; i++) {
- // ... 处理网络事件
- }
- // 处理定时事件
- update_timer();
- }
例如:skynet,...;在单独的线程来检测定时器。通过usleep来触发定时器,定时任务的执行通过信号或者插入执行队列让其他线程执行。
- // 网络事件和定时事件在不同线程中处理
- void * thread_timer(void *thread_param) {
- init_timer();
- while (!quit) {
- update_timer();
- sleep(t);
- }
- clear_timer();
- return NULL;
- }
- pthread_create(&pid, NULL, thread_timer,&thread_param);
- // 初始化定时器
- void init_timer();
- // 添加定时器
- Node* add_timer(int expire, callback cb);
- // 删除定时器
- bool del_timer(Node* node);
- // 找到最近要触发的定时任务
- Node* find_nearest_timer();
- // 更新检测定时器
- void update_timer();
- // 清除定时器
- // void clear_timer();
对定时任务的组织本质是要处理对定时任务优先级的处理;由此产生两类数据结构;
1.按触发时间进行顺序组织
要求数据结构有序(红黑树、跳表),或者相对有序(最小堆);
能快速查找最近触发的定时任务;
需要考虑怎么处理相同时间触发的定时任务;
2.按执行顺序进行组织
时间轮
选择set容器(红黑树实现)
- //获取当前时间
- //定义静态成员,类共享
- static time_t GetTick() {
- //chrono是c++ 11中的时间库,提供计时,时钟等功能
- //毫秒:std::chrono::milliseconds
- //time_point_cast对时间点进行转换
- //chrono::steady_clock进行程序耗时的时长,只要启动就会进行时间的累加,并且不能被修改,非常适合于进行耗时的统计。
- auto sc = chrono::time_point_cast<chrono::milliseconds>(chrono::steady_clock::now());
- //time_since_epoch 获取对象经过的时间间隔
- auto temp = chrono::duration_cast<chrono::milliseconds>(sc.time_since_epoch());
- return temp.count();
- }
- #include <sys/epoll.h>
- #include <functional>
- #include <chrono>
- #include <set>
- #include <memory>
- #include <iostream>
-
- using namespace std;
-
-
- struct TimerNodeBase {
- time_t expire;
- int64_t id;
- };
-
- //公有继承
- struct TimerNode : public TimerNodeBase {
- //std::function是一个可调用对象包装器,是一个类模板
- //相当于 typedef std::function<void(const TimerNode &node)> Callback;
- using Callback = std::function<void(const TimerNode &node)>;
- Callback func;
-
- //结构体定义构造函数
- //成员func = 形参func
- TimerNode(int64_t id, time_t expire, Callback func) : func(func) {
- //形参名与成员变量名相同,则用{this->成员变量名=形参名;}
- this->expire = expire;
- this->id = id;
- }
- };
-
- //运算符重载
- bool operator < (const TimerNodeBase &lhd, const TimerNodeBase &rhd) {
- if (lhd.expire < rhd.expire)
- return true;
- else if (lhd.expire > rhd.expire)
- return false;
- return lhd.id < rhd.id;
- }
-
-
- class Timer {
- public:
- static time_t GetTick() {
- //chrono是c++ 11中的时间库,提供计时,时钟等功能。
- //毫秒:std::chrono::milliseconds
- //time_point_cast对时间点进行转换
- //chrono::steady_clock进行程序耗时的时长,只要启动就会进行时间的累加,并且不能被修改,非常适合于进行耗时的统计。
- auto sc = chrono::time_point_cast<chrono::milliseconds>(chrono::steady_clock::now());
- //time_since_epoch 获取对象经过的时间间隔
- auto temp = chrono::duration_cast<chrono::milliseconds>(sc.time_since_epoch());
- return temp.count();
- }
-
- //添加
- TimerNodeBase AddTimer(time_t msec, TimerNode::Callback func) {
- time_t expire = GetTick() + msec;
- //emplace() 返回的迭代器指向新插入的元素
- auto ele = timermap.emplace(GenID(), expire, func);
- return static_cast<TimerNodeBase>(*ele.first);
- }
-
- //移除
- bool DelTimer(TimerNodeBase &node) {
- auto iter = timermap.find(node);
- if (iter != timermap.end()) {
- timermap.erase(iter);
- return true;
- }
- return false;
- }
-
- //查询
- bool CheckTimer() {
- auto iter = timermap.begin();
- if (iter != timermap.end() && iter->expire <= GetTick()) {
- iter->func(*iter);
- timermap.erase(iter);
- return true;
- }
- return false;
- }
-
- //时间差
- time_t TimeToSleep() {
- auto iter = timermap.begin();
- if (iter == timermap.end()) {
- return -1;
- }
- time_t diss = iter->expire-GetTick();
- return diss > 0 ? diss : 0;
- }
- private:
- static int64_t GenID() {
- return gid++;
- }
- static int64_t gid;
-
- //std::less<T>是一个类的名称,是C++官方给你写好了的一个Comparator,里面的operator()也帮你重载好了的。
- set<TimerNode, std::less<>> timermap;
- };
- int64_t Timer::gid = 0;
-
-
- int main() {
- int epfd = epoll_create(1);
-
- unique_ptr<Timer> timer = make_unique<Timer>();
-
- int i =0;
-
- //[&]:以引用形式捕获所有外部变量,也就是外部变量均可用
- timer->AddTimer(1000, [&](const TimerNode &node) {
- cout << Timer::GetTick() << "node id:" << node.id << " revoked times:" << ++i << endl;
- });
-
- timer->AddTimer(1000, [&](const TimerNode &node) {
- cout << Timer::GetTick() << "node id:" << node.id << " revoked times:" << ++i << endl;
- });
-
- timer->AddTimer(3000, [&](const TimerNode &node) {
- cout << Timer::GetTick() << "node id:" << node.id << " revoked times:" << ++i << endl;
- });
-
- //auto:当编译器能够在一个变量的声明时候就推断出它的类型,那么你就能够用auto关键字来作为他们的类型
- auto node = timer->AddTimer(2100, [&](const TimerNode &node) {
- cout << Timer::GetTick() << "node id:" << node.id << " revoked times:" << ++i << endl;
- });
-
- timer->DelTimer(node);
-
- cout << "now time:" << Timer::GetTick() << endl;
- epoll_event ev[64] = {0};
-
- while (true) {
- /*
- -1 永久阻塞
- 0 没有事件立刻返回,有事件就拷贝到 ev 数组当中
- t > 0 阻塞等待 t ms,
- timeout 最近触发的定时任务离当前的时间
- */
- int n = epoll_wait(epfd, ev, 64, timer->TimeToSleep());
- for (int i = 0; i < n; i++) {
- /**/
- }
- /* 处理定时事件*/
- while(timer->CheckTimer());
- }
-
- return 0;
- }
std::function对C++中各种可调用实体(普通函数、Lambda表达式、函数指针、以及其它函数对象等)的封装,形成一个新的可调用的std::function对象,简化调用
- # include <iostream>
- # include <functional>
-
- typedef std::function<int(int, int)> comfun;
-
- // 普通函数
- int add(int a, int b) { return a + b; }
-
- // lambda表达式
- auto mod = [](int a, int b){ return a % b; };
-
- // 函数对象类
- struct divide{
- int operator()(int denominator, int divisor){
- return denominator/divisor;
- }
- };
-
- int main(){
- comfun a = add;
- comfun b = mod;
- comfun c = divide();
- std::cout << a(5, 3) << std::endl;
- std::cout << b(5, 3) << std::endl;
- std::cout << c(5, 3) << std::endl;
- }
lambda是匿名的,lambda表达式就是一段可调用的代码。主要适合于只用到一两次的简短代码段。
- # include <iostream>
- int fun3(int x, int y){
- auto f = [](int x, int y) { return x + y; }; //创建lambda表达式,如果参数列表为空,可以省去()
- std::cout << f(x, y) << std::endl; //调用lambda表达式
- }
- int main(){
- fun3(300, 300);
- }
类似于成员函数,但是在初始化成员方面,构造函数显然更方便。
注意:
1. 构造函数冒号的左边和构造函数相同,名字与结构体名相同,还有一个形参列表
2. 如果形参名与成员变量名不同,则可以用成员变量名(形参名)(示例grade变量),多个成员用逗号分割。如果形参名与成员变量名相同,则用{this->成员变量名=形参名;}(示例name变量)来初始化,注意用“->”访问成员变量
3. 可以直接用变量名(值)(示例name变量)直接给变量赋值
4. {}一定要有,即使没有用到this来初始化变量,也要加上{}
5. 形参列表可以为空,然后用第3点的方法初始化变量
6. 若无构造函数,系统会赠送初始值
7. this是指向本身的指针,所以访问成员变量用->
1. 命令空间的using声明
using std::cin; //必须每一个都有独立的using声明
using std::cout; using std::endl; //写在同一行也需要独立声明
2. 在子类中引用基类成员
3. 使用using起别名
typedef std::vector
intvec; using intvec = std::vector
; //这两个写法是等价的
推荐一个不错的学习网站 C/C++后台高级服务器https://ke.qq.com/course/417774?flowToken=1010783