ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

定时器的使用

2021-09-16 16:02:17  阅读:278  来源: 互联网

标签:调用 定时器 时间 timeout 使用 定时 超时


前言

一般在游戏开发或者网络程序中会使用到定时器,当然最先接触定时器的还是JS中setTimeout和setInterval。同理其他语言中有时也需要使用到定时器。而在Linux中提供了三种定时方法,它们是:

  • socket选项SO_RCVTIMEO和SO_SNDTIMEO。
  • SIGALRM信号。
  • I/O复用系统调用的超时参数。

定时方法

socket选项SO_RCVTIMEO和SO_SNDTIMEO

SO_RCVTIMEO和SO_SNDTIMEO是分别用来设置socket接收数据超时时间和发送数据超时时间。因此,这两个选项仅对与数据接收和发送相关的socket专用系统调用有效,这些系统调用包括send、sendmsg、recv、recvmsg、accept和connect。

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

在程序中,我们可以根据系统调用(send、sendmsg、recv、recvmsg、accept和connect)的返回值以及errno来判断超时时间是否已到,进而决定是否开始处理定时任务。

SIGALRM信号

由alarm和 setitimer函数设置的实时闹钟一旦超时,将触发SIGALRM信号。因此,我们可以利用该信号的信号处理函数来处理定时任务。但是,如果要处理多个定时任务,我们就需要不断地触发SIGALRM信号,并在其信号处理函数中执行到期的任务。

I/O复用系统调用的超时参数

Linux下的3组IO复用系统调用都带有超时参数,因此它们不仅能统一处理信号和IO事件,也能统一处理定时事件。但是由于IO复用系统调用可能在超时时间到期之前就返回(有IO事件发生),所以如果我们要利用它们来定时,就需要不断更新定时参数以反映剩余的时间。

#define TIMEOUT 5000

int timeout = TIMEOUT;
time_t start = time(NULL);
time_t end = time(NULL);
while(1)
{
	printf("the timeout is now %d mil-seconds\n",timeout);
	start = time(NULL);
	int number = epoll_wait(epollfd,evenets,MAX_EVENT_NUMBER,timeout);
	if((number < 0) && (errno != EINTR))
	{
		printf("epoll failure\n");
		break;
	}
	//如果epoll_wait成功返回0,则说明超时时间到,此时便可处理定时任务,并重置定时时间
	if(number == 0)
	{
		timeout = TIMEOUT;
		continue;
	}
	end = time(NULL);
	//如果epoll_wait的返回值大于0,则本次epoll_wait调用持续的时间是(end-start)*1000ms,我们需要将定时时间timeout减去这段时间,以获得下次epoll_wait调用的超时参数
	timeout -= (end-start)*1000;
	//重新计算之后的timeout值有可能等于0,说明本次epoll_wait调用返回时,不仅有文件描述符就绪,而且其超时时间也刚好到达,此时我们也要处理定时任务,并重置定时时间
	if(timeout <= 0)
	{
		timeout = TIMEOUT;
	}
	
}

高性能定时器

时间轮

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

对于时间轮而言,要提高定时精度,就要使槽间隔si足够小;要提高执行效率,则要求槽的个数N足够大。

时间堆

前面讨论的定时方案都是以固定的频率调用心搏函数tick,并在其中依次检测到期的定时器,然后执行到期定时器上的回调函数。
设计定时器的另外一种思路是:将所有定时器中的超时时间最小的一个定时器的超时值作为心搏间隔。这样,一旦心搏函数tick被调用,超时时间最小的定时器必然到期,我们就可以在tick函数中处理该定时器。然后,再次从剩余的定时器中找出超时时间最小的一个,并将这段最小时间设置为下一次心搏间隔。如此反复,就实现了较为精确的定时。

资料

《Linux高性能服务器编程》

标签:调用,定时器,时间,timeout,使用,定时,超时
来源: https://blog.csdn.net/XZ2585458279/article/details/120327617

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有