http://www.cppblog.com/CppExplore/archive/2008/04/02/46111.html
一、 基础知识
1、时间类型。Linux下常用的时间类型有4个:time_t,struct timeval,struct timespec,struct tm。
(1)time_t是一个长整型,一般用来表示用1970年以来的秒数。
(2)Struct timeval有两个成员,一个是秒,一个是微妙。
struct timeval {
long tv_sec; /**//* seconds */
long tv_usec; /**//* microseconds */
}; (3)struct timespec有两个成员,一个是秒,一个是纳秒。
struct timespec{
time_t tv_sec; /**//* seconds */
long tv_nsec; /**//* nanoseconds */
}; (4)struct tm是直观意义上的时间表示方法:
struct tm {
int tm_sec; /**//* seconds */
int tm_min; /**//* minutes */
int tm_hour; /**//* hours */
int tm_mday; /**//* day of the month */
int tm_mon; /**//* month */
int tm_year; /**//* year */
int tm_wday; /**//* day of the week */
int tm_yday; /**//* day in the year */
int tm_isdst; /**//* daylight saving time */
}; 2、 时间操作
(1) 时间格式间的转换函数
主要是 time_t、struct tm、时间的字符串格式之间的转换。看下面的函数参数类型以及返回值类型:
char *asctime(const struct tm *tm);
char *ctime(const time_t *timep);
struct tm *gmtime(const time_t *timep);
struct tm *localtime(const time_t *timep);
time_t mktime(struct tm *tm); gmtime和localtime的参数以及返回值类型相同,区别是前者返回的格林威治标准时间,后者是当地时间。
(2) 获取时间函数
两个函数,获取的时间类型看原型就知道了:
time_t time(time_t *t);
int gettimeofday(struct timeval *tv, struct timezone *tz); 前者获取time_t类型,后者获取struct timeval类型,因为类型的缘故,前者只能精确到秒,后者可以精确到微秒。
二、 延迟函数
主要的延迟函数有:sleep(),usleep(),nanosleep(),select(),pselect().
unsigned int sleep(unsigned int seconds);
void usleep(unsigned long usec);
int nanosleep(const struct timespec *req, struct timespec *rem);
int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,struct timeval *timeout);
int pselect(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask); alarm函数是信号方式的延迟,这种方式不直观,这里不说了。
仅通过函数原型中时间参数类型,可以猜测sleep可以精确到秒级,usleep/select可以精确到微妙级,nanosleep和pselect可以精确到纳秒级。
而实际实现中,linux上的nanosleep和alarm相同,都是基于内核时钟机制实现,受linux内核时钟实现的影响,并不能达到纳秒级的精度,man nanosleep也可以看到这个说明,man里给出的精度是:Linux/i386上是10 ms ,Linux/Alpha上是1ms。
这里有一篇文章http://blog.csdn.net/zhoujunyi/archive/2007/03/30/1546330.aspx,测试了不同延迟函数之间的精确度。文章给出的结论是linux上精度最高的是select,10ms级别。我在本机器测试select和pselect相当,都达到了1ms级的精度,精度高于文章中给出的10ms,sleep在秒级以上和usleep/nanosleep相当。下面贴下我机器上1ms时候的测试结果,其他不贴了:
sleep 1000 0 -1000
usleep 1000 2974 1974
nanosleep 1000 2990 1990
select 1000 991 -9
pselect 1000 990 -10
gettimeofday 1000 1000 0 而使用gettimeofday循环不停检测时间,可精确微秒级,不过不适宜用来做定时器模块。
因此后面的定时期模块将选择select为延迟函数。
三、 定时器模块需求以及实现概述
1、需求。从实现结果的角度说来,需求就是最终的使用方式。呵呵,不详细描述需求了,先直接给出我实现的CTimer类的三个主要接口:
Class CTimer{
Public:
CTimer(unsigned int vinterval,void (*vfunc)(CTimer *,void *),void *vdata,TimerType vtype);
void start();
void stop();
void reset(unsigned int vinterval);
}; 使用定时器模块的步骤如下:
(1) 实例化一个CTimer,参数的含义依次是:vinterval间隔时间(单位ms),vfunc是时间到回调的函数,vdata回调函数使用的参数,vtype定时器的类型,分一次型和循环型两种。
(2) 调用start方法。
(3) 必要的时候调用stop和reset。
2、实现。简单描述下定时器模块的实现,有一个manager单例类保存所有CTimer对象,开启一线程运行延迟函数,每次延迟间隔到,扫描保存CTimer的容器,对每个CTimer对象执行减少时间操作,减少到0则执行回调函数。对一次性CTimer,超时则从容器中删除,循环型的将间隔时间重置,不从容器中移除。
CTimer的start执行将对象插入到manager容器中操作;stop执行将对象从manager容器中删除的操作;reset执行先删除,重置间隔,然后再放到容器中,reset不改变CTimer的定时器类型属性。
四、 定时器模块的数据结构选择
Manager类的容器要频繁进行的操作涉及插入、删除、查询等。
误区:(1)简单看,好象该容器要是有序的,方便插入删除等,貌似红黑树比较合适。其实不然,插入删除操作的频率很低,最频繁的还是每次时延到,对容器的扫描并做时间减少操作,红黑树在做顺序扫描相对链表并没什么优势。
(2) 插入的时候依照顺序链表的方式插入到合适的位置保持排序,以保证超时的对象都在链表的头端。其实这也是没必要的,每次时延到,对每一个对象都要做时间减少操作,因此不管是有序还是无序,都是一次扫描就执行完下面操作:减少时间、判断是否超时,是则执行回调,继续判断是什么类型,一次型的则执行完就移除,循环型则执行完直接重置间隔就可。
因此,只需要能快速插入头、删除结点、遍历就好。我的实现直接使用BSD内核中的数据结构LIST,插入头、删除时间复杂度都是1,遍历就不说了。linux下/usr/include/sys下有头文件queue.h里也有LIST结构以及操作的定义。貌似linux下的少了遍历宏:
#define LIST_FOREACH(var, head, field) \
for((var) = LIST_FIRST(head); \
(var)!= LIST_END(head); \
(var) = LIST_NEXT(var, field)) 五、 详细实现
这里帖出主要的代码,请重点关注CTimerManager:: process方法,不再详细说了。需要详细的全部代码,可来信索取,整体代码很简单,就两个类。
class CTimer
{
friend class CTimerManager;
public:
typedef enum
{
TIMER_IDLE=0, //start前以及手动调用stop后的状态
TIMER_ALIVE, //在manager的list里时候的状态
TIMER_TIMEOUT //超时后被移除的状态,循环型的没有
}TimerState;
typedef enum
{
TIMER_ONCE=0, //一次型
TIMER_CIRCLE //循环型
}TimerType;
CTimer(unsigned int vinterval,void (*vfunc)(CTimer *,void *),void *vdata,TimerType vtype);
void start();
void stop();
void reset(unsigned int vinterval);
~CTimer();
private:
unsigned int id_; //测试用
unsigned int m_interval; //间隔,不变
unsigned int m_counter; //开始设置为interval,随延迟时间到,减少
TimerState m_state; //状态
TimerType m_type; //类型
void (*m_func)(CTimer *,void *);//回调函数
void * m_data; //回调函数参数
LIST_ENTRY(CTimer) entry_; //LIST的使用方式
};
/**//*构造函数*/
CTimer::CTimer(unsigned int vinterval,void (*vfunc)(CTimer *,void *),void *vdata,TimerType vtype):
m_interval(vinterval),m_counter(vinterval),
m_state(TIMER_IDLE),m_type(vtype),
m_func(vfunc),m_data(vdata)
{}
/**//*开始定时器*/
void CTimer::start()
{
CTimerManager::instance()->add_timer(this);
}
/**//*停止定时器*/
void CTimer::stop()
{
CTimerManager::instance()->remove_timer(this);
}
/**//*reset定时器*/
void CTimer::reset(unsigned int vinterval)
{
CTimerManager::instance()->remove_timer(this);
m_counter=m_interval=vinterval;
CTimerManager::instance()->add_timer(this);
}
/**//*析构函数,stop操作不能省略,避免delete前忘记stop*/
CTimer::~CTimer()
{
if(m_state==TIMER_ALIVE)
stop();
} CTimerManager的:
class CTimerManager
{
public:
typedef enum
{
TIMER_MANAGER_STOP=0,
TIMER_MANAGER_START
}TimerManagerState;
static CTimerManager * instance();
void add_timer(CTimer * vtimer);//线程安全的add
void remove_timer(CTimer * vtimer);//线程安全的remove
void start(); //开始process线程
void stop(); //停止process线程
void dump();
protected:
static void * process(void *); //实际的定时器时间延迟线程
private:
void add_timer_(CTimer * vtimer);//非线程安全的add
void remove_timer_(CTimer * vtimer);//非线程安全的remove
CTimerManager();
static pthread_mutex_t m_mutex;
static CTimerManager * m_instance;
TimerManagerState m_state;
LIST_HEAD(,CTimer) list_; //LIST使用方式
static unsigned int mark; //测试,配合dump
};
/**//*singlton的double-check实现*/
CTimerManager * CTimerManager::instance()
{
if(m_instance==NULL)
{
pthread_mutex_lock(&m_mutex);
if(m_instance==NULL)
{
m_instance=new CTimerManager();
}
pthread_mutex_unlock(&m_mutex);
}
return m_instance;
}
/**//*process必须static,不能操作非static属性,因此传递this指针*/
void CTimerManager:: start()
{
if(m_state==TIMER_MANAGER_STOP){
m_state=TIMER_MANAGER_START;
pthread_t pid;
pthread_create(&pid,0,process,this);
}
}
/**//*定时器模块延迟时间线程*/
void * CTimerManager:: process(void * arg)
{
pthread_detach(pthread_self());
CTimerManager *manage=(CTimerManager *)arg;
CTimer *item;
struct timeval start,end;
unsigned int delay;
struct timeval tm;
gettimeofday(&end,0);
/**//*使用状态控制线程运行,进而容易实现stop,也可以使用pthread_cancel粗暴的停止,需要考虑暂停点等问题*/
while(manage->m_state==TIMER_MANAGER_START)
{
tm.tv_sec=0;
tm.tv_usec=DEFULT_INTERVAL*1000;
start.tv_sec=end.tv_sec;
start.tv_usec=end.tv_usec;
/**//*不同系统的延迟函数精度不同,如果需要替换为其他延迟函数,这附近修改下就好*/
while(select(0,0,0,0,&tm)<0&&errno==EINTR);
gettimeofday(&end,0);
delay=(end.tv_sec-start.tv_sec)*1000+(end.tv_usec-start.tv_usec)/1000;
pthread_mutex_lock(&manage->m_mutex);
LIST_FOREACH(item, &(manage->list_), entry_)
{
item->m_counter<delay?item->m_counter=0:item->m_counter-=delay;
if(item->m_counter==0)
{
if(item->m_func)
item->m_func(item,item->m_data);
if(item->m_type==CTimer::TIMER_ONCE)
{
/**//*一次型的,超时,移除,并状态CTimer::TIMER_TIMEOUT*/
manage->remove_timer_(item);
item->m_state=CTimer::TIMER_TIMEOUT;
}
else if(item->m_type==CTimer::TIMER_CIRCLE)
{
/**//*循环型的,重置counter就好*/
item->m_counter=item->m_interval;
}
}
}
pthread_mutex_unlock(&manage->m_mutex);
}
}
六、 讨论
(1)精度问题。精度高,实时性高,但要求select等待的时间缩短,进而增加对LIST结构的扫描操作。精度低,实时性差,但会增加定时器线程的睡眠时间,减少对cpu的占用。一般的应用系统,应该尽量降低精度,避免不必要的扫描,对具体系统可考察所用到的所有定时器的实际间隔,在允许的情况下,尽量降低精度,可通过修改代码中的宏实现。为了降低定时器线程对cpu的占有时间,甚有更为粗犷型的定时器模块实现为将延迟时间取list中最小的那个间隔,保证每次延迟时间到都有回调。
(2)加锁区域问题。本文中的定时器模块实现,将定时器对象的时间减少以及函数回调的执行等再同一个临界区内执行,而有的定时器模块实现是在加锁区域执行“时间减少”操作,将减少到0的对象放到另一个超时链表中,解锁后再单独扫描超时链表执行回调操作。很明显,后者缩短了加锁时间,能及时响应其他的线程的定时器对象的start以及stop操作。但是后者对定时器操作的时序性有误差,直观反应就是可能在定时器执行了stop操作以后,仍然会有超时回调发生,特别是回调参数是指针的情况,可能引起难以发现的bug,增加调试困难。在衡量两者的利弊后,本文采用延长加锁时间以保证操作的时序性。因此,在实际的使用,回调函数应尽快返回,另一方面,尽量减少系统内使用的定时器数目,这个主要原因是延迟时间到要扫描LIST,哪种方式都避免不了。
七、使用示例
#include "timer_manager.h"
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
void func(CTimer * timer, void *data)
{
printf("hi,%d\n",(int)(data));
}
/**//*随便写的,凑合着看吧。没有CTimerManager::instance()->stop();也没new对象。定时器对象可多次start和stop,使用上对暴露的接口没有任何的契约式限制,可随意调用*/
int main()
{
CTimerManager::instance()->start();
CTimer a(1000,func,(void *)1,CTimer::TIMER_CIRCLE);
CTimer a1(2000,func,(void *)11,CTimer::TIMER_ONCE);
CTimer a2(3000,func,(void *)12,CTimer::TIMER_ONCE);
CTimer a3(1000,func,(void *)13,CTimer::TIMER_ONCE);
a.start();
a1.start();
a2.start();
a3.start();
a.start();
a1.start();
a2.start();
a3.start();
sleep(1);
CTimerManager::instance()->dump();
sleep(1);
CTimerManager::instance()->dump();
a.reset(2000);
a1.stop();
a3.stop();
sleep(10);
return 0;
}
八、后记
昨晚写好文章,不知为何无故丢失大半,郁闷。今早醒来,感觉还是有点地方要修改,:在start定时器线程的时候,传入精度和误差补偿,一是根据实际需要调整精确度,二是弥补延迟函数的稍许误差,以具有更好的伸缩性和精确度。本文旨在说明定时器模块的内部实现机制,详细细节不再修改了。
http://muscle-liu.javaeye.com/blog/413353
在多线程的应用中要用到延时函数,开始时我只用到 sleep 这个秒级函数,但在 solaris 上跑时,程序运行到sleep时,却显示 “Alarm clock” 这句话后就中止了。据说是产生了 alarm 这个信号,而系统默认信号处理就是中止程序,所以要在程序中把这个设置为忽略:
- signal(SIGALRM, SIG_IGN);
signal(SIGALRM, SIG_IGN);
unix 上的延时函数有好几种:
引用
一、 基础知识
1、时间类型。Linux下常用的时间类型有4个:time_t,struct timeval,struct timespec,struct tm。
(1)time_t是一个长整型,一般用来表示用1970年以来的秒数。
(2)Struct timeval有两个成员,一个是秒,一个是微妙。
- struct timeval {
- long tv_sec;
- long tv_usec;
- ;
struct timeval {
long tv_sec; /**//* seconds */
long tv_usec; /**//* microseconds */
};
(3)struct timespec有两个成员,一个是秒,一个是纳秒。
- struct timespec{
- time_t tv_sec;
- long tv_nsec;
- };
struct timespec{
time_t tv_sec; /**//* seconds */
long tv_nsec; /**//* nanoseconds */
};
(4)struct tm是直观意义上的时间表示方法:
- struct tm {
- int tm_sec;
- int tm_min;
- int tm_hour;
- int tm_mday;
- int tm_mon;
- int tm_year;
- int tm_wday;
- int tm_yday;
- int tm_isdst;
- };
struct tm {
int tm_sec; /**//* seconds */
int tm_min; /**//* minutes */
int tm_hour; /**//* hours */
int tm_mday; /**//* day of the month */
int tm_mon; /**//* month */
int tm_year; /**//* year */
int tm_wday; /**//* day of the week */
int tm_yday; /**//* day in the year */
int tm_isdst; /**//* daylight saving time */
};
2、 时间操作
(1) 时间格式间的转换函数
主要是 time_t、struct tm、时间的字符串格式之间的转换。看下面的函数参数类型以及返回值类型:
- char *asctime(const struct tm *tm);
- char *ctime(const time_t *timep);
- struct tm *gmtime(const time_t *timep);
- struct tm *localtime(const time_t *timep);
- time_t mktime(struct tm *tm);
char *asctime(const struct tm *tm);
char *ctime(const time_t *timep);
struct tm *gmtime(const time_t *timep);
struct tm *localtime(const time_t *timep);
time_t mktime(struct tm *tm);
gmtime和localtime的参数以及返回值类型相同,区别是前者返回的格林威治标准时间,后者是当地时间。
(2) 获取时间函数
两个函数,获取的时间类型看原型就知道了:
- time_t time(time_t *t);
- int gettimeofday(struct timeval *tv, struct timezone *tz);
time_t time(time_t *t);
int gettimeofday(struct timeval *tv, struct timezone *tz);
前者获取time_t类型,后者获取struct timeval类型,因为类型的缘故,前者只能精确到秒,后者可以精确到微秒。
二、 延迟函数
主要的延迟函数有:sleep(),usleep(),nanosleep(),select(),pselect().
- unsigned int sleep(unsigned int seconds);
-
- void usleep(unsigned long usec);
-
- int nanosleep(const struct timespec *req, struct timespec *rem);
-
- int select(int n, fd_set *readfds,fd_set *writefds,fd_set *exceptfds,
- struct timeval *timeout);
-
- int pselect(int n,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,
- const struct timespec *timeout, const sigset_t *sigmask);
unsigned int sleep(unsigned int seconds);
void usleep(unsigned long usec);
int nanosleep(const struct timespec *req, struct timespec *rem);
int select(int n, fd_set *readfds,fd_set *writefds,fd_set *exceptfds,
struct timeval *timeout);
int pselect(int n,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,
const struct timespec *timeout, const sigset_t *sigmask);
alarm函数是信号方式的延迟,这种方式不直观,这里不说了。
仅通过函数原型中时间参数类型,可以猜测sleep可以精确到秒级,usleep/select可以精确到微妙级,nanosleep和pselect可以精确到纳秒级。
而 实际实现中,linux上的nanosleep和alarm相同,都是基于内核时钟机制实现,受linux内核时钟实现的影响,并不能达到纳秒级的精度, man nanosleep也可以看到这个说明,man里给出的精度是:Linux/i386上是10 ms ,Linux/Alpha上是1ms。
这里有一篇文章http://blog.csdn.net/zhoujunyi/archive/2007/03/30/1546330.aspx, 测试了不同延迟函数之间的精确度。文章给出的结论是linux上精度最高的是select,10ms级别。我在本机器测试select和pselect相 当,都达到了1ms级的精度,精度高于文章中给出的10ms,sleep在秒级以上和usleep/nanosleep相当。下面贴下我机器上1ms时候 的测试结果,其他不贴了:
- sleep 1000 0 -1000
- usleep 1000 2974 1974
- nanosleep 1000 2990 1990
- select 1000 991 -9
- pselect 1000 990 -10
- gettimeofday 1000 1000 0
sleep 1000 0 -1000
usleep 1000 2974 1974
nanosleep 1000 2990 1990
select 1000 991 -9
pselect 1000 990 -10
gettimeofday 1000 1000 0
而使用gettimeofday循环不停检测时间,可精确微秒级,不过不适宜用来做定时器模块。
因此后面的定时期模块将选择select为延迟函数。
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/wbj1234566/archive/2008/05/13/2442264.aspx
我在用 usleep 时却发现有部分线程完全在等待中,没有醒过来, 最后换用了 nanosleep
正常回了。注意,要调用 nanosleep, 编译时要带 -lposix4
nanosleep 的例子(来自http://hi.baidu.com/zengzhaonong/blog/item/2fa4a799e282bb096f068c62.html)
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <sys/time.h>
- #include <sched.h>
- #define COUNT 1000
- #define MILLION 1000000L
-
- int main(void)
- {
- int i;
- struct timespec slptm;
- long tdif;
- struct timeval tend, tstart;
-
- slptm.tv_sec = 0;
- slptm.tv_nsec = 1000;
-
-
-
-
-
- if (gettimeofday(&tstart, NULL) == -1) {
- fprintf(stderr, "Failed to get start time\n");
- return 1;
- }
- for (i = 0; i < COUNT; i++) {
- if (nanosleep(&slptm, NULL) == -1) {
- perror("Failed to nanosleep");
- return 1;
- }
- }
- if (gettimeofday(&tend, NULL) == -1) {
- fprintf(stderr, "Failed to get end time\n");
- return 1;
- }
- tdif = MILLION * (tend.tv_sec - tstart.tv_sec) + (tend.tv_usec - tstart.tv_usec);
- printf("nanosleep() time is %ld us\n", tdif/COUNT);
- return 0;
- }
-
-
-
- HZ 250HZ
- 时钟中断的时间间隔: 4 ms (1000ms/250)
- ----------------------------------------
- nanosleep() time is 4019 us (4.019 ms)
- 说明nanosleep的睡眠定时器依赖于时钟中断
-
-
-
- HZ 1000HZ
- 时钟中断的时间间隔: 1 ms
- ----------------------------------------
- nanosleep() time is 12 us
- 注: 最小睡眠时间为1 us
http://bbs.chinaunix.net/viewthread.php?tid=340598&extra=&page=1
测试
IBM AIX 3.4 单CPU
sleep 可以在多线程中使用,只阻塞本线程,不影响所属进程中的其它线程
不支持 nanosleep
支持 usleep 和 select
以下采用 gettimeofday 对 usleep 和 select 的实际精确情况进行测试分析
function time(usec) realTime reduce
-------------------------------------------------------------------
usleep 500000 500026 26
nanosleep 500000 not support
select 500000 500026 26
usleep 100000 100021 21
nanosleep 100000 not support
select 100000 100025 25
usleep 50000 50021 21
nanosleep 50000 not support
select 50000 50107 107
usleep 10000 10099 99
nanosleep 10000 not support
select 10000 10025 25
usleep 1000 1021 21
nanosleep 1000 not support
select 1000 1024 24
usleep 900 920 20
nanosleep 900 not support
select 900 1024 124
usleep 500 523 23
nanosleep 500 not support
select 500 1024 524
usleep 100 119 19
nanosleep 100 not support
select 100 1023 923
usleep 10 31 21
nanosleep 10 not support
select 10 1024 1014
usleep 1 19 18
nanosleep 1 not support
select 1 1026 1025
由此可以得出,在AIX 3.4下:
select 只能精确到毫秒级别
usleep 可以精确到微秒级
在1毫秒以上,两者的精确度基本一样
同上,在 linux 2.4.20-8smp 双CPU 下测试
function time(usec) realTime reduce
-------------------------------------------------------------------
usleep 500000 506453 6453
nanosleep 500000 509930 9930
select 500000 499990 -10
usleep 100000 110023 10023
nanosleep 100000 109955 9955
select 100000 99992 -8
usleep 50000 59971 9971
nanosleep 50000 59990 9990
select 50000 50025 25
usleep 10000 19991 9991
nanosleep 10000 19988 9988
select 10000 9956 -44
usleep 1000 19990 18990
nanosleep 1000 19989 18989
select 1000 10024 9024
usleep 900 20009 19109
nanosleep 900 19972 19072
select 900 9943 9043
usleep 500 19975 19475
nanosleep 500 19971 19471
select 500 10012 9512
usleep 100 19975 19875
nanosleep 100 19976 19876
select 100 9943 9843
usleep 10 19988 19978
nanosleep 10 19961 19951
select 10 10011 10001
usleep 1 19978 19977
nanosleep 1 19985 19984
select 1 9932 9931
在 2.4.21-4.ELsmp #1 SMP 4 CPU 下测试
function time(usec) realTime reduce
-------------------------------------------------------------------
usleep 500000 501267 1267
nanosleep 500000 509964 9964
select 500000 499981 -19
usleep 100000 109944 9944
nanosleep 100000 109925 9925
select 100000 99963 -37
usleep 50000 59904 9904
nanosleep 50000 59973 9973
select 50000 49956 -44
usleep 10000 19988 9988
nanosleep 10000 20008 10008
select 10000 10020 20
usleep 1000 19988 18988
nanosleep 1000 19980 18980
select 1000 9943 8943
usleep 900 19975 19075
nanosleep 900 19986 19086
select 900 9905 9005
usleep 500 19989 19489
nanosleep 500 19910 19410
select 500 10000 9500
usleep 100 19355 19255
nanosleep 100 19902 19802
select 100 9988 9888
usleep 10 19977 19967
nanosleep 10 19988 19978
select 10 9943 9933
usleep 1 20007 20006
nanosleep 1 19947 19946
select 1 9980 9979
由此可以得出如下结论,在 linux 2.4 下:
1、支持 usleep,nanosleep,select
2、select 的 精确度为 10毫秒。在10毫秒以上很精确
3、usleep, nanosleep 很不精确
同样,通过其它测试程序能得出如下结论:
sleep 可以在多线程中使用,只阻塞本线程,不影响所属进程中的其它线程
我只有以上3种测试环境,有其它测试环境的帮我在其它环境下测试一下,将分析结果贴出来。
测试程序在之后发上来
再论精确延时(usleep,nanosleep,select)/*
make: gcc -o test_sleep test_sleep.c
*/
/* #include "comm_main.h" */
#include <stdio.h>;
#include <stdlib.h>;
#include <time.h>;
#include <sys/time.h>;
#include <errno.h>;
#include <string.h>;
#include <unistd.h>;
#include <sys/types.h>;
#define PRINT_USEAGE { \
fprintf(stderr,"\n Usage: %s usec ",argv[0]); \
fprintf(stderr,"\n\n"
;\
}
int
main (int argc, char **argv)
{
unsigned int nTimeTestSec = 0; /* sec */
unsigned int nTimeTest = 0; /* usec */
struct timeval tvBegin;
struct timeval tvNow;
int ret = 0;
unsigned int nDelay = 0; /* usec */
fd_set rfds;
struct timeval tv;
int fd = 1;
int i = 0;
struct timespec req;
unsigned int delay[20] =
{ 500000, 100000, 50000, 10000, 1000, 900, 500, 100, 10, 1, 0 };
int nReduce = 0; /* 误差 */
#if 0
if (argc < 2)
{
PRINT_USEAGE;
exit (1);
}
nDelay = atoi (argv[1]);
#endif
fprintf (stderr, "%18s%12s%12s%12s\n", "function", "time(usec)", "realTime",
"reduce"
;
fprintf (stderr,
"-------------------------------------------------------------------\n"
;
for (i = 0; i < 20; i++)
{
if (delay
<= 0)
break;
nDelay = delay;
/* test usleep */
gettimeofday (&tvBegin, NULL);
ret = usleep (nDelay);
if (-1 == ret)
{
fprintf (stderr, " usleep error . errno=%d [%s]\n", errno,
strerror (errno));
}
gettimeofday (&tvNow, NULL);
nTimeTest =
(tvNow.tv_sec - tvBegin.tv_sec) * 1000000 + tvNow.tv_usec -
tvBegin.tv_usec;
nReduce = nTimeTest - nDelay;
fprintf (stderr, "\t usleep %8u %8u %8d\n", nDelay, nTimeTest,
nReduce);
/* test nanosleep */
gettimeofday (&tvBegin, NULL);
req.tv_sec = nDelay / 1000000;
req.tv_nsec = (nDelay % 1000000) * 1000;
ret = nanosleep (&req, NULL);
if (-1 == ret)
{
fprintf (stderr, "\t nanosleep %8u not support\n", nDelay);
}
else
{
gettimeofday (&tvNow, NULL);
nTimeTest =
(tvNow.tv_sec - tvBegin.tv_sec) * 1000000 + tvNow.tv_usec -
tvBegin.tv_usec;
nReduce = nTimeTest - nDelay;
fprintf (stderr, "\t nanosleep %8u %8u %8d\n", nDelay,
nTimeTest, nReduce);
}
/* test select */
gettimeofday (&tvBegin, NULL);
FD_ZERO (&rfds);
FD_SET (fd, &rfds);
tv.tv_sec = 0;
tv.tv_usec = nDelay;
ret = select (0, NULL, NULL, NULL, &tv);
if (-1 == ret)
{
fprintf (stderr, " select error . errno=%d [%s]\n", errno,
strerror (errno));
}
gettimeofday (&tvNow, NULL);
nTimeTest =
(tvNow.tv_sec - tvBegin.tv_sec) * 1000000 + tvNow.tv_usec -
tvBegin.tv_usec;
nReduce = nTimeTest - nDelay;
fprintf (stderr, "\t select %8u %8u %8d\n", nDelay, nTimeTest,
nReduce);
}
return 0;
}
文章评论(0条评论)
登录后参与讨论