在项目的开发过程中,经常会遇到使用定时器的情况。定时器,顾名思意,就是定时定点工作的一种机制。定时器这个东西,无论是在军事、工业甚至是航天飞行的尖端科技上都是必不可缺的。在国人心中大长志气的北斗卫星,其核心也是有一个授时系统,你也可以简单理解成一个定时器(这不太准确,人家那个复杂的多)。
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打断一下看看效果,如果不想被打断怎么办?前面提到过返回值代表余下的时间,可以用一个循环将剩余的时间执行完成即可(注释部分)。
定时器的用法看似挺基础,其实在开发中,应用的极其普遍,在其中演化的各种应用也相当复杂,同学们如果有兴趣可以看看网上高手们的代码,你就会发现,写代码其实既容易,又极其不容易。要想达到后者,就得把前者搞得明明白白白,透透彻彻。