当前位置: 首页 > news >正文

一文搞定Linux的定时器(19)

定时器是什么

网络程序需要处理的第三类事件是定时事件,比如定期检测一个客户连接的活动状态。服务器程序通常管理着众多定时事件,因此有效地组织这些定时事件,使之能在预期的时间点被触发且不影响服务器的主要逻辑,对于服务器的性能有着至关重要的影响。为此,我们要将每个定时事件分别封装成定时器,并使用某种容器类数据结构,比如链表、排序链表和时间轮,将所有定时器串联起来,以实现对定时事件的统一管理。本博客主要讨论的就是两种高效的管理定时器的容器:
时间轮和时间堆。

首先什么是定时

不过,在讨论如何组织定时器之前,我们先要介绍定时的方法。定时是指在一段时间之后触发某段代码的机制,我们可以在这段代码中依次处理所有到期的定时器。换言之,定时机制是定时器得以被处理的原动力。Linux提供了三种定时方法,它们是:
socket选项SO_RCVTIMEO 和so_SNDTIMEO。
SIGALRM信号。
IO复用系统调用的超时参数。

SO_RCVTIMEO 和so_SNDTIMEO

这个博客中我们介绍过socket选项So_RCVTIMEO和SO_SNDTIMEO,它们分别用来设置socket接收数据超时时间和发送数据超时时间。因此,这两个选项仅对与数据接收和发送相关的socket专用系统调用有效,这些系统调用包括send、sendmsg、recv、recvmsg、accept和 connect。我们将选项SO_RCVTIMEO和SO_SNDTIMEO对这些系统调用的影响总结于下表中

系统调用有效选项系统调用超时后的行为
sendSO_SNDTIMEO返回-1,设置errno为EAGAIN或EWOULDBLOCK
sendmsgSO_SNDTIMEO返回-1,设置errno为EAGAIN或EWOULDBLOCK
recvSO_RCVTIMEO返回-1,设置errno为EAGAIN或EWOULDBLOCK
recvmsgSO_RCVTIMEO返回-1,设置errno为EAGAIN或EWOULDBLOCK
accpteSO_RCVTIMEO返回-1,设置errno为EAGAIN或EWOULDBLOCK
connectSO_SNDTIMEO返回-1,设置errno为EINPROGRRRESS

SIGALRM

由alarm和 setitimer函数设置的实时闹钟一旦超时,将触发SIGALRM信号。因此,我们可以利用该信号的信号处理函数来处理定时任务。但是,如果要处理多个定时任务,我们就需要不断地触发SIGALRM信号,并在其信号处理函数中执行到期的任务。一般而言,SIGALRM信号按照固定的频率生成,即由alarm或setitimer函数设置的定时周期T保持不变。如果某个定时任务的超时时间不是T的整数倍,那么它实际被执行的时间和预期的时间将略有偏差。因此定时周期T反映了定时的精度。

高性能定时器

时间轮(基于hash表实现)

时间轮如图:

在这里插入图片描述
上图图所示的时间轮内,(实线)指针指向轮子上的一个槽(slot)。它以恒定的速度顺时针转动,每转动一步就指向下一个槽(虚线指针指向的槽),每次转动称为一个滴答( tick)。一个滴答的时间称为时间轮的槽间隔si (slot interval),它实际上就是心搏时间。该时间轮共有N个槽,因此它运转一周的时间是Nsi。每个槽指向一条定时器链表,每条链表上的定时器具有相同的特征:**它们的定时时间相差Nsi 的整数倍**。时间轮正是利用这个关系将定时器散列到不同的链表中。
假如现在指针指向槽cs,我们要添加一个定时时间为ti的定时器,则该定时器将被插入槽ts ( timer slot)对应的链表中:
ts=(cs+(ti/si))%N
基于排序链表的定时器使用唯一的一条链表来管理所有定时器**,所以插入操作的效率随着定时器数目的增多而降低**。而时间轮使用哈希表的思想,将定时器散列到不同的链表上。这样每条链表上的定时器数目都将明显少于原来的排序链表上的定时器数目,插人操作的效率基本不受定时器数目的影响。

笔者将实现时间轮代码封装一下 (未完成)

时间堆(最小堆思路)

设计定时器的另外一种思路是﹔将所有定时器中超时时间最小的一个定时器的超时值作为心搏间隔。这样,一旦心搏函数tick被调用,超时时间最小的定时器必然到期,我们就可以在tick 函数中处理该定时器。然后,再次从剩余的定时器中找出超时时间最小的一个,并将这段最小时间设置为下一次心搏间隔。如此反复,就实现了较为精确的定时。
在这里插入图片描述
树的基本操作是插人节点和删除节点。对最小堆而言,它们都很简单。为了将一个元素X插入最小堆,我们可以在树的下一个空闲位置创建一个空穴。如果X可以放在空穴中而不破坏堆序,则插入完成。否则就执行上虑操作,即交换空穴和它的父节点上的元素。不断执行上述过程,直到X可以被放入空穴,则插入操作完成。比如,我们上图要往图所示的最小堆中插入值为14的元素,则可以按照下图所示的步骤来操作
在这里插入图片描述
最小堆的删除操作指的是删除其根节点上的元素,并且不破坏堆序性质。执行删除操作时,我们需要先在根节点处创建一个空穴。由于堆现在少了一个元素,因此我们可以把堆的最后一个元素X移动到该堆的某个地方。如果X可以被放入空穴,则删除操作完成。否则就执行下虑操作,即交换空穴和它的两个儿子节点中的较小者。不断进行上述过程,直到X可以被放入空穴,则删除操作完成。比如,我们
上上图来实现delete
在这里插入图片描述
由于最小堆是一种完全二叉树,所以我们可以用数组来组织其中的元素。比如,所示的最小堆可以用下图所示的数组来表示。对于数组中的任意一个位置i上的元素,其左儿子节点在位置24+1上,其右儿子节点在位置2i+2上,其父节点则在位置〔(i-1)/2 ](i0)上。与用链表来表示堆相比,用数组表示堆不仅节省空间,而且更容易实现堆的插人、删除等操作
在这里插入图片描述
假设我们已经有一个包含N个元素的数组,现在要把它初始化为一个最小堆。那么最简单的方法是﹔
初始化一个空堆,然后将数组中的每个元素插人该堆中。不过这样做的效率偏低。实际上,**我们只需要对数组中的第〔(N-1)/2]0个元素执行下虑操作,即可确保该数组构成一个最小堆**。这是因为对包含N个元素的完全二叉树而言,它具有[(N-1)/2]个非叶子节点,这些非叶子节点正是该完全二叉树的第0[(N-1)/2]个节点。我们只要确保这些非叶子节点构成的子树都具有堆序性质,整个树就具有堆序性质。
笔者实现封装时间堆(未完成)

相关文章:

  • 物联网的常用几种协议
  • URDMA跑起来
  • 商业银行云模式下的技术变革
  • go的解析命令行库flag
  • idea jsp文件 高亮_有了这几款idea插件后,同事再也不叫我小白了
  • 猿创征文|Mybatis注解完成增删改查操作
  • Code For Better ---- 拥抱TensorFlow 拥抱未来
  • 【SpringBoot】SpringBoot自定义banner,成千上万种可供选择,当然也可以自定义生成哦
  • 物联网面试题之如果有二维数组int arr[3][4]和如果有数组int arr[5]
  • Hadoop和Spark的对比
  • 智能座舱行为识别数据解决方案,助力打造第三空间新体验
  • centos8同步时间安装时间校准服务
  • PHP 图像处理组件:Intervention/image
  • java幼儿园信息管理系统
  • 599. 两个列表的最小索引总和
  • C++入门教程(10):for 语句
  • Docker下部署自己的LNMP工作环境
  • ESLint简单操作
  • export和import的用法总结
  • golang 发送GET和POST示例
  • Joomla 2.x, 3.x useful code cheatsheet
  • LintCode 31. partitionArray 数组划分
  • Linux学习笔记6-使用fdisk进行磁盘管理
  • Terraform入门 - 1. 安装Terraform
  • Vue官网教程学习过程中值得记录的一些事情
  • 阿里研究院入选中国企业智库系统影响力榜
  • 机器人定位导航技术 激光SLAM与视觉SLAM谁更胜一筹?
  • 聊聊flink的BlobWriter
  • 爬虫进阶 -- 神级程序员:让你的爬虫就像人类的用户行为!
  • 排序算法之--选择排序
  • 视频flv转mp4最快的几种方法(就是不用格式工厂)
  • 智能合约开发环境搭建及Hello World合约
  • 自制字幕遮挡器
  • ​iOS实时查看App运行日志
  • # 执行时间 统计mysql_一文说尽 MySQL 优化原理
  • #1015 : KMP算法
  • #DBA杂记1
  • (2)nginx 安装、启停
  • (aiohttp-asyncio-FFmpeg-Docker-SRS)实现异步摄像头转码服务器
  • (floyd+补集) poj 3275
  • (Java数据结构)ArrayList
  • (Matalb时序预测)WOA-BP鲸鱼算法优化BP神经网络的多维时序回归预测
  • (poj1.3.2)1791(构造法模拟)
  • (PWM呼吸灯)合泰开发板HT66F2390-----点灯大师
  • (附源码)计算机毕业设计SSM智能化管理的仓库管理
  • (介绍与使用)物联网NodeMCUESP8266(ESP-12F)连接新版onenet mqtt协议实现上传数据(温湿度)和下发指令(控制LED灯)
  • (六)库存超卖案例实战——使用mysql分布式锁解决“超卖”问题
  • (没学懂,待填坑)【动态规划】数位动态规划
  • (每日持续更新)jdk api之FileReader基础、应用、实战
  • (十二)springboot实战——SSE服务推送事件案例实现
  • (十五)devops持续集成开发——jenkins流水线构建策略配置及触发器的使用
  • (新)网络工程师考点串讲与真题详解
  • (一)kafka实战——kafka源码编译启动
  • (转)es进行聚合操作时提示Fielddata is disabled on text fields by default
  • (转)淘淘商城系列——使用Spring来管理Redis单机版和集群版