2013-08-16 48 views
7

背景:我有一个计时器,我用它来跟踪自从serialPort DataReceived事件被触发以来它已经持续了多长时间。我正在创建自己的解决方案,而不是使用内置的超时事件,因为我正在获取连续的数据流,而不是发送查询并获得一个响应。System.Timers.Timer在timer.Stop()被调用后执行已经执行的事件

问题: 在DataReceived处理程序中,我有一条语句来停止计时器,以便不会过去。问题是很多时候它仍然执行Elapsed处理程序。

我读过,这是可以使用SynchronizingObject来解决这个问题,但我不知道如何做到这一点。

这是我的代码:我试图剪掉所有我认为不相关的东西。

private System.Timers.Timer timeOut; 
    private System.Timers.Timer updateTimer; 

    public void start() 
    { 
     thread1 = new Thread(() => record()); 

     thread1.Start(); 
    } 

    public void requestStop() 
    { 
     this.stop = true; 
     this.WaitEventTest.Set(); 

    } 

    private void record() 
    { 
     timeOut = new System.Timers.Timer(500); //** .5 Sec 
     updateTimer = new System.Timers.Timer(500); //** .5 Sec 

     timeOut.Elapsed += TimeOut_Elapsed; 
     updateTimer.Elapsed += updateTimer_Elapsed; 
     updateTimer.AutoReset = true; 


     comport.Open(); 
     comport.DiscardInBuffer(); 


     comport.Write(COMMAND_CONTINUOUSMODE + "\r"); 

     stopwatch.Reset(); 
     stopwatch.Start(); 

     recordingStartTrigger(); //** Fire Recording Started Event 

     timeOut.Start(); 
     updateTimer.Start(); 

     this.waitHandleTest.WaitOne(); //** wait for test to end 

     timeOut.Stop(); 
     updateTimer.Stop(); 

     comport.Write(COMMAND_COMMANDMODE + Environment.NewLine); 
     comport.DiscardInBuffer(); 
     comport.Close(); 
     recordingStopTrigger(status); //** Fire Recording Stopped Event 

     stopwatch.Stop(); 
    } 


    //*********************************************************************************** 
    //** Events Handlers 


    private void comDataReceived_Handler(object sender, SerialDataReceivedEventArgs e) 
    { 

     double force = -100000; 
     string temp = "-100000"; 

     //timeOut.SynchronizingObject.Invoke(new Action(()=> {timeOut.Stop();}), new object[] {sender, e}); 

     timeOut.Stop(); 

     //** I removed my action code here, keep things simple. 


     timeOut.Start(); 
    } 

    private void TimeOut_Elapsed(object sender, System.Timers.ElapsedEventArgs e) 
    { 
     timeOut.Stop(); 
     updateTimer.Stop(); 


     //** fire delegate that GUI will be listening to, to update graph. 
     if (eventComTimeOut != null && this.stop == false) 
     { 
      if (eventComTimeOut(this, new eventArgsComTimeOut(comport.PortName, "READ"))) 
      { 
       //retry = true; 
       comport.Write(COMMAND_CONTINUOUSMODE + "\r"); 
       updateTimer.Start(); 
       timeOut.Start(); 
      } 
      else 
      { 
       this.stop = true; 
       //retry = false; 
       this.WaitEventTest.Set(); 
       status = eventArgsStopped.Status.failed;      
      } 
     } 
    } 

    void updateTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) 
    { 

     //** fire delegate that GUI will be listening to, to update graph. 
     List<Reading> temp = new List<Reading>(report.Readings_Force); 
     eventNewData(this, new eventArgsNewData(temp)); 

    } 

回答

24

这是众所周知的行为。 System.Timers.Timer内部使用ThreadPool执行。运行时将在线程池中排列Timer。在您调用Stop方法之前,它已经排队。它会在经过的时间点燃。

为避免发生这种情况,请将Timer.AutoReset设置为false,并在需要的处理程序中启动计时器。设置AutoReset false使定时器只触发一次,所以为了让定时器在间隔时间内手动启动定时器启动定时器。

yourTimer.AutoReset = false; 

private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) 
{ 
    try 
    { 
     // add your logic here 
    } 
    finally 
    { 
     yourTimer.Enabled = true;// or yourTimer.Start(); 
    } 
} 
+2

@Downvoter有何评论? –

+0

最后加入try的原因是什么? –

+5

@mikejames在'try'块中,你会添加你的逻辑,即使在例外的情况下,finally块也会确保你的定时器将再次启动。 –

2

我在这段代码中做了一个暂停计时器。对我来说这是有效的。

Private cTimer As New System.Timers.Timer 
Private Sub inittimer() 
    cTimer.AutoReset = True 
    cTimer.Interval = 1000 
    AddHandler cTimer.Elapsed, AddressOf cTimerTick 
    cTimer.Enabled = True 
End Sub 

Private Sub cTimerTick() 
    If cTimer.AutoReset = True Then 
     'do your code if not paused by autoreset false 
    End If 
End Sub