定时器精度对性能的影响_Linux下几种定时器的使用

80c01ef7e73b32de84917c71b3b8bc81.png

一、定时器

在项目的开发过程中,经常会遇到使用定时器的情况。定时器,顾名思意,就是定时定点工作的一种机制。定时器这个东西,无论是在军事、工业甚至是航天飞行的尖端科技上都是必不可缺的。在国人心中大长志气的北斗卫星,其核心也是有一个授时系统,你也可以简单理解成一个定时器(这不太准确,人家那个复杂的多)。
Linux下,由于开源的问题,平台甚多,导致大量的类似或者相近的定时器函数,但其实常用的只有几种,其中有一些在man文档中已经明确指出不建议再使用,当然还有一些高手自己编写了自己的高精度定时器,但弄明白了下面的这几种,基本就好理解他们的了。

二、主要的定时器类型

1、sleep():
在不同的Gcc版本编译器,使用的头文件可能略有不同,如果不清楚,查一下资料即可。它主要是让当前线程睡眠指定的秒数,注意是second,秒。其函数原型是:

 unsigned int sleep(unsigned int seconds);

其返回值代表被打断定时后,剩余的定时器时间,和下面的定时器有相同的效果。
看一个例子:

#include   

 #include 
 #include
 int main(){
  int a = 1;
  printf("test");
  sleep(a);        /* VC 使用Sleep*/
  printf("ok");
  return 0;
 }

它的缺点是精度不太高,而且容易受信号机制影响,太多定时器有可能导致进程的整体挂起。

2、usleep():
这个函数精度比较高,但是基本各个平台都已经不再推荐使用。它是非线程安全的。不过值得一提的是,虽然精度高,但精度却不怎么靠谱。其函数原型如下:

 #include 
int  usleep(int micro_seconds);

usleep功能把进程挂起一段时间, 单位是微秒(百万分之一秒)。如果返回0表示成功,否则表示出现了错误,可以查看ERRORS的信息来确定错误原因。

3、nanosleep()
这个函数是内核从2.0时才提供的一个函数,这个可以推荐使用替代usleep()这个函数,它的精度更高到了纳秒(1秒= 1000^3纳秒)。它可以使当前进程暂停到指定时间后恢复执行。调用nanosleep将导致进程进入TASK_INTERRUPTIBLE,从而使信号进入TASK_RUNNING状态。所以一定要小心,这极有可能在没有到达指定时间前而被其它信号唤醒。可以通过判断返回值来判断是否出现这种情况(返回-1),在这种情况下,可以在rem中查询剩余的时间。它的函数原型是:

int nanosleep(const struct timespec * req, struct timespec * rem);

struct timespec
{
    time_t  tv_sec;         // seconds
    long    tv_nsec;        // nanoseconds
};

这个函数有点一定要记清楚,这个和下面的select()函数有些类似,就是你的时间值要不断的重设才能保证你的时间的间隔的准确性(虽然他不太准确),原因是,他有可能在信号被触发后返回时,保留住了余下的时间,再回去仍然会继续这个时间。不过sleep() usleep()都是调用这个函数的。
4、select():
这个函数其实非常强大的,学过网络编程的同学们可能都知道它可以做一种网络通信机制存在,当然,这里它也是可以做为定时器函数使用的。它的函数原型如下:

int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);

简单的例子:

struct timval tv;
while(true)
{

  tv.tv_sec = 2;
  tv.tv_usec = 0;
  int ret = select(maxfd+1, &readfd, NULL, NULL, &tv);
};

在前面提到过,select函数中的最后一个参数,需要在循环中设置,每次循环要重新设置。如果设在循环外面,当循环执行起来后,每次循环select都会修改tv的值,tv的值越来越小,导致最后会产生select函数这tv时间内收不到有效时间,而返回-1,造成错误。linux系统对select()的实现中会修改参数tv为剩余时间。所以在循环内部使用函数select的时候一定要在循环内部初始化时间参数。

三、分析

以上就是常见的定时器使用的方法,LINUX下开源,所以也没有什么必须一说,要找最合适的。函数nanosleep()是在POSIX规范中的,该函数可以替代usleep(),它的精度是纳秒级,虽然在上面也说明了它的缺点,但是还是相对来说比较好的。在Solaris的多线程环境下编译器会自动把usleep()连接成nanosleep()。短时间的延时,推荐使用Linux下用select函数,它的定时精度相对来说还是准确的.
不过一定要注意的是,这种函数再精确,其实还是要根据具体的硬件来确定,毕竟所有的OS的定时器的定时精度都是从硬件来发生的。这种情况在开发嵌入式程序时需要特别注意。在普通PC上开发,一般来说没有什么这方面的瓿。在这些函数之外,可能还会有相类似的函数,比如:pselect(),poll等,都可以做为定时器,还有一些通过click函数来达到类似定时器效果的都可以,在实际情况中,自由把握。
最后再提醒一下,要多看文档,现在版本更迭的太快,一定不要大意。

四、信号打断

在前面提到了,定时器函数有可能被其它信号打断,从而导致精度不准确,下面给出一个例子,有兴趣可以在电脑上测试一下就明白了

#include
#include 
#include 
#include 

void test_handler(int sig){
    printf(" signal number is %d\n", sig);
}

int main(int argc,char**argv){
    unsigned int time = 10;

    signal(SIGINT, test_handler);
    printf("start timer!\n");

    unsigned int ret = sleep(time);

    /*
    do{
        ret = sleep(time);
    }while(ret > 0);
    /*
    printf("end timer!-- %u\n",ret);
    return 0;
}

你可以在程序运行是使用ctrl+c打断一下看看效果,如果不想被打断怎么办?前面提到过返回值代表余下的时间,可以用一个循环将剩余的时间执行完成即可(注释部分)。

五、总结

定时器的用法看似挺基础,其实在开发中,应用的极其普遍,在其中演化的各种应用也相当复杂,同学们如果有兴趣可以看看网上高手们的代码,你就会发现,写代码其实既容易,又极其不容易。要想达到后者,就得把前者搞得明明白白白,透透彻彻。

6ca378d23b2cf38ffa0fd530980887ed.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值