您的位置:程序门 -> .net技术 -> c#



如何在c#中实现精确timer?


[收藏此页] [打印本页]选择字色:背景色:字体:[][][]


如何在c#中实现精确timer?[已结贴,结贴人:vinbenlee]
发表于:2007-04-03 14:47:36 楼主
近来在做一个关于音乐合成的东西,其中需要较高的时间精度,利用timer,发现每10秒左右总会有毫秒级的误差……

请问还有没有更精确一点的计时器?
希望各位能够贴上一段代码,帮我解决这个问题!谢谢!!
发表于:2007-04-03 15:17:131楼 得分:0
各位大哥大姐,拜托你们了!谢谢!!
发表于:2007-04-03 15:26:362楼 得分:0
system.threading.timer
比timer控件精确!
发表于:2007-04-03 15:27:143楼 得分:0
计时器常用的只有timer和线程了,不过平时没有考虑到误差的问题,
如果计时器不成你可以试下用线程来计时,   不过估计也有误差的
发表于:2007-04-03 15:31:034楼 得分:0
用线程吧,用gettickcount()判断时间差
发表于:2007-04-03 15:31:315楼 得分:0
秒不正确

就精确到   毫秒   微妙啊

直接用   毫秒   数值表示   不是更精确了...


发表于:2007-04-03 15:34:536楼 得分:80
关于c#中timer类     在c#里关于定时器类就有3个      
1.定义在system.windows.forms里      
2.定义在system.threading.timer类里      
3.定义在system.timers.timer类里    
system.windows.forms.timer是应用于winform中的,它是通过windows消息机制实现的,类似于vb或delphi中的timer控件,内部使用api     settimer实现的。它的主要缺点是计时不精确,而且必须有消息循环,console     application(控制台应用程序)无法使用。      
   
system.timers.timer和system.threading.timer非常类似,它们是通过.net     thread     pool实现的,轻量,计时精确,对应用程序、消息没有特别的要求。system.timers.timer还可以应用于winform,完全取代上面的timer控件。它们的缺点是不支持直接的拖放,需要手工编码。  
例:  
使用system.timers.timer类  
system.timers.timer   t   =   new   system.timers.timer(10000);//实例化timer类,设置间隔时间为10000毫秒;  
t.elapsed   +=   new   system.timers.elapsedeventhandler(theout);//到达时间的时候执行事件;  
t.autoreset   =   true;//设置是执行一次(false)还是一直执行(true);  
t.enabled   =   true;//是否执行system.timers.timer.elapsed事件;  
public   void   theout(object   source,   system.timers.elapsedeventargs   e)  
{  
messagebox.show( "ok! ");  
}      


关于定时器
定时器是个很有意思的东西,它很有用,但我认为这不是现代计算机的结构所擅长的事情。
计算机适合做那些很大量的简单重复工作,或者根据请求做出回应。
dos时代是没有进程线程等概念的,那时候要想做到定时真是有些麻烦
通常的做法是死循环不断监测时间,发现时间到了就做特定的事情
当然你可以用delay,来指定等待多长时间,但是如果你一边要响应用户的操作,比如输入,一边要定时做些
事情就是一件麻烦的事了
当然有些人可以这样做,截取系统的时钟中断(我忘了中断号是多少了),每秒钟有18.2次
当这些做法都不是很优雅。但dos时代只能这样凑合着了
windows是个伟大的进步,系统提供了timer支持,但是问题是这个定时器并不准时而且有时候根本不能用。
win32   api中有个settimer函数,可以为一个窗口创建一个定时器,这个定时器会定时产生消息wm_timer也可以调用
指定的回调函数,其实这都是一样的,因为都是单线程的。
单线程的定时器会有很多问题,首先是不准时,定时器只是定时把消息wm_timer访到线程的消息队列里,但是并不保证消息会立刻被响应,如果
碰巧系统比较忙,那么消息可能会在队列里放一端时间才被响应,还会造成本来应该间隔一段时间发生的消息响应连续发生了
解决方法通常是
ontimer(...)
{
  //timer   process.....
 
  msg   msg;
  while(peekmessage(&msg,   m_hwnd,   wm_timer,   wm_timer,   pm_remove));
}
在当前timer处理中,把消息队列里的wm_timer消息,清除掉。
更糟的是如果你不去调用getmessage,那么就不会有timer发生了。
这个问题直到win   xp都没什么改变,似乎微软并不打算在win32   api中解决这个问题了。
.net   framework为我们带来了新的解决方案
.net   framework提供三种timer
server   timers                 system.timers.timer
thread   timers               system.threading.timer  
windows   timers       system.windows.forms.timer
其中windows   timers只是提供了和winapi   一样的timer,仍然是基于消息,仍然是单线程
其它两个就不同了,他们是基于线程池的thread   pool,这样最大的好处在于,产生的时间间隔准确均匀。
server   timers     和   thread   timers   的不同在于servertimers   是基于事件的,thread   timers是基于回调函数
我更喜欢thread   timer,比较轻量级方便易用。
但是这样的timer也有问题,就是由于时多线程定时器,就会出现如果一个timer处理没有完成,到了时间下一个
照样会发生,这就会导致重入问题
对付重入问题通常的办法是加锁,但是对于   timer却不能简单的这样做,你需要评估一下
首先timer处理里本来就不应该做太需要时间的事情,或者花费时间无法估计的事情,比同远方的服务器建立一个网络连接,这样的做法尽量避免
如果实在无法避免,那么要评估timer处理超时是否经常发生,如果是很少出现,那么可以用lock(object)的方法来防止重入
如果这种情况经常出现呢?那就要用另外的方法来防止重入了
我们可以设置一个标志,表示一个timer处理正在执行,下一个timer发生的时候发现上一个没有执行完就放弃执行
static     int   intimer   =   0;
public   static   void   threadtimercallback(object   obj)
{
          if   (   intiemr   ==   0   )
        {
                  intimer   =   1;                  
                  console.writeline( "time:{0},   \tthread   id:{1} ",   datetime.now,   thread.currentthread.gethashcode());
                  thread.sleep(2000);
                  intimer   =   0;
            }
}
但是在多线程下给intimer赋值不够安全,还好interlocked.exchange提供了一种轻量级的线程安全的给对象赋值的方法
  static   int   intimer   =   0;
  public   static   void   threadtimercallback(object   obj)
  {
              if   (   interlocked.exchange(ref   intimer,   1)   ==   0   )
            {
                      console.writeline( "time:{0},   \tthread   id:{1} ",   datetime.now,   thread.currentthread.gethashcode());
                      thread.sleep(250);
                      interlocked.exchange(ref   intimer,   0);
            }
  }


正确的选择使用.net中的三个timer  
timer这个类在.net的类库中有三个:

1)system.threading.timer  

            是一个使用回调方法的计时器,而且由线程池线程服务,简单且对资源要求不高。

2)system.windows.forms.timer  

          这是一个必须和windows窗体一起使用的timer。

3)system.timers.timer

            基于服务器计时器功能的timer,根据服务器系统时间进行运行的timer。如果需要写windows   services的话可以使用这个timer来进行一

些需要在一定间隔时间进行某项操作的环境下使用。
            它使您能够指定在应用程序中引发   elapsed   事件的周期性间隔。然后可以操控此事件以提供定期处理。例如,假设您有一台关键性服务

器,必须每周   7   天、每天   24   小时都保持运行。可以创建一个使用   timer   的服务,以定期检查服务器并确保系统开启并在运行。如果系统不

响应,则该服务可以尝试重新启动服务器或通知管理员。基于服务器的   timer   是为在多线程环境中用于辅助线程而设计的。服务器计时器可

以在线程间移动来处理引发的   elapsed   事件,这样就可以比   windows   计时器更精确地按时引发事件。

发表于:2007-04-03 15:37:577楼 得分:0
我觉得可以自己做一个类似游戏中控制贞数的东西
发表于:2007-04-03 15:40:498楼 得分:0
api,取cpu频率
发表于:2007-04-03 15:52:279楼 得分:0
看能否找到更精确的控件,
发表于:2007-04-03 16:01:2910楼 得分:0
mark
发表于:2007-04-03 16:04:2411楼 得分:0
baidu下   我记得cnblog上有     取cpu频率的
发表于:2007-04-03 16:48:2312楼 得分:0
对,用system.threading.timer   可以精确到毫秒.
发表于:2007-04-03 16:51:4313楼 得分:0
gettickcount()绝对够精确
发表于:2007-04-03 17:02:2714楼 得分:0
thread的sleep方法试看.


快速检索

最新资讯
热门点击