2011-06-23 44 views
3

我试图创建一个派生类的Timer,它允许设置一个'Pause'闩锁以防止工作线程重新激活定时器。但是,当AutoReset设置为false并且Enabled访问器似乎正在执行阻止在设置Paused变量时修改基类的Enabled属性的工作时,Elapsed事件继续发生。当Enabled设置为false时,为什么System.Timer.Timer仍然触发事件?

为什么会发生这种情况,或者应该使用什么策略来进一步了解这里实际发生的交互?

我附上了下面派生类的实现。

using System.Timers 
    class PauseableTimer : Timer 
    { 
     public bool Paused; 
     new public bool Enabled 
     { 
     get 
     { 
      return base.Enabled; 
     } 
     set 
     { 
      if (Paused) 
      { 
      if (!value) base.Enabled = false; 
      } 
      else 
      { 
      base.Enabled = value; 
      } 
     } 
     } 
    } 

举例说明问题。

class Program 
{ 
    private static PauseableTimer _pauseableTimer; 
    private static int _elapsedCount; 
    static void Main(string[] args) 
    { 
     _pauseableTimer = new PauseableTimer(){AutoReset = false,Enabled = false,Paused = false}; 

     _pauseableTimer.Elapsed += pauseableTimer_Elapsed; 
     _pauseableTimer.Interval = 1; 
     _pauseableTimer.Enabled = true; 
     while(_elapsedCount<100) 
     { 
      if (_elapsedCount > 50) _pauseableTimer.Paused = true; 
     } 
    } 

    static void pauseableTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) 
    { 
     Console.WriteLine(String.Format("this.Enabled:'{0}',Paused:'{1}',AutoReset:'{2}",_pauseableTimer.Enabled,_pauseableTimer.Paused,_pauseableTimer.AutoReset)); 
     _elapsedCount++; 
     _pauseableTimer.Interval = _pauseableTimer.Interval == 1 ? 2 : 1; //This line breaks it. 
     _pauseableTimer.Enabled = true; 
    } 
} 

回答

4

Relevant document, System.Timers.Timer.Interval

注 如果启用和自动复位都设置为false,定时器先前已启用,设置间隔属性会导致一旦引发Elapsed事件,如如果Enabled属性已被设置为true。要设置间隔而不引发事件,可以暂时将AutoReset属性设置为true。

设置AutoReset为true,因为有一个事件处理程序还允许被触发的事件过程中设置AutoReset到真正的无证行为不会解决问题的建议解决方案。

解决方案似乎是建立派生的对象,你可以保留任何显然很多方式的事件可以再次发生。

下面是我结束的实现。

public class PauseableTimer : Timer 
{ 
    private bool _paused; 
    public bool Paused 
    { 
     get { return _paused; } 
     set 
     { 
      Interval = _interval; 
      _paused = value; 
     } 
    } 

    new public bool Enabled 
    { 
     get 
     { 
      return base.Enabled; 
     } 
     set 
     { 
      if (Paused) 
      { 
       if (!value) base.Enabled = false; 
      } 
      else 
      { 
       base.Enabled = value; 
      } 
     } 
    } 

    private double _interval; 
    new public double Interval 
    { 
     get { return base.Interval; } 
     set 
     { 
      _interval = value; 
      if (Paused){return;} 
      if (value>0){base.Interval = _interval;} 
     } 
    } 

    public PauseableTimer():base(1){} 

    public PauseableTimer(double interval):base(interval){} 
} 
+0

正是我看到的问题。底线:不要设置时间间隔,否则计时器将会启动。 –

4

恐怕所有的东西在多线程中都比较复杂。假设您的代码按照您的意愿工作,那么在重置Enabled属性后,会出现一个窗口,其中可以提升进行中的事件。看到这个从MSDN docs的报价。

提高Elapsed事件 的信号总是排队上 线程池线程执行。在Enabled属性设置为 false之后,可能会导致Elapsed事件中的 被触发 。 Stop 方法的代码示例显示了解决此竞争条件的一种方法。

+0

我在尝试暂停它后没有收到一个事件触发。我得到许多事件发射。一个事件被解雇是可以接受的,我只是不希望它继续为所有永恒开火。 – Erick

+0

在这种情况下,我认为你的重置'Enabled'的逻辑必须是错误的。你可以在执行逻辑之前修改你的'OnElapsed'事件来检查它吗?例如,在上面的代码中,我看不出'暂停'如何。 –

+0

“已暂停”是一个公共变量,由客户端对象修改。我已经在调试器下运行代码,并验证一旦'Paused'设置为true,访问器不允许将'Enabled'设置为true。我不知道有一种方法让调试器在“Elapsed”事件被触发之前停止。 – Erick

-1

我会重新格式化您的代码:

// from this 
if (!value) base.Enabled = false; 

// to this 
if (!value) 
    base.Enabled = false; 

它不仅读更好的,你可以把一个破发点上重点线,看看它是否正在执行

+1

C#中的断点(或至少在Visual Studio中)不一定绑定到一行。无论格式如何,您都可以在语句上设置断点。 – Joey

+0

您可以设置一个断点,但不知道是否由于if或赋值而停止。在一行代码中放置两个逻辑步骤是一种不好的做法。 –

+0

大括号?为了更好的代码可读性? –

2

另一种选择是压制事件???我无法解释发生了什么,但下面提出的理论应该可以让你绕开你所讨论的这个小问题。正如史蒂夫提到的那样,在你设置的“已启用属性”上放置了一个'Watch and break point',并确保它已被设置。

我将如何解决这个:

捕捉并检查“已启用”属性,并删除“ - =”订阅方法(处理)在需要时再重新添加“+ =”当你需要处理'Elapsed'事件时,它又是一次。

我已经在几个不同的WinForms项目上多次使用过这种风格。如果您不希望以编程方式处理'Elapsed'事件,则在满足特定条件时创建一个检查并将其删除,然后在满足相反条件时添加它。

if (paused) // determine pause logic to be true in here 
{ 
    timer.Elapsed -= ... // remove the handling method. 
} 
else 
{ 
    timer.Elapsed += ... // re-add it in again 
} 

上面的代码逻辑将允许您在代码忽略'Elapsed'事件时,在'Paused'标志为真时引发'Elapsed'事件。希望以上有助于

+0

这似乎是一个合理的方式来做到这一点。我对能够维护事件处理程序的准确列表有一些担忧,但这些似乎是可解决的问题。我仍然想明白为什么我会看到我所看到的行为。如果找不到我目前看到的内容的解决方案,我可能会使用您的解决方案。谢谢。 – Erick

相关问题