在其回调方法中停止计时器

时间:2009-11-09 07:15:34

标签: c# callback timer reentrancy

我有一个System.Threading.Timer,每隔<10秒调用一次适当的事件处理程序(回调)。该方法本身不可重入,有时可以方式超过10毫秒。因此,我想在方法执行期间停止计时器。

代码:

private Timer _creatorTimer;

// BackgroundWorker's work
private void CreatorWork(object sender, DoWorkEventArgs e) {
      _creatorTimer = new Timer(CreatorLoop, null, 0, 10);

      // some other code that worker is doing while the timer is active
      // ...
      // ...
}

private void CreatorLoop(object state) {
      // Stop timer (prevent reentering)
      _creatorTimer.Change(Timeout.Infinite, 0);

      /*
          ... Work here
      */

      // Reenable timer
      _creatorTimer.Change(10, 0);
} 

MSDN声明在线程池的单独线程中调用回调方法(每次定时器触发)。这意味着如果我在方法中停止计时器,它仍然不会阻止计时器触发并在第一个有机会停止计时器之前运行该方法的另一个实例。

是否应该锁定计时器(甚至是非重入方法本身)? 在执行其回调(和非重入)方法期间阻止计时器被触发的正确方法是什么?

5 个答案:

答案 0 :(得分:46)

您可以让计时器继续触发回调方法,但将不可重入的代码包装在Monitor.TryEnter / Exit中。在这种情况下无需停止/重启计时器;重叠的呼叫不会获得锁定并立即返回。

 private void CreatorLoop(object state) 
 {
   if (Monitor.TryEnter(lockObject))
   {
     try
     {
       // Work here
     }
     finally
     {
       Monitor.Exit(lockObject);
     }
   }
 }

答案 1 :(得分:6)

几种可能的解决方案:

  • 在另一个正在等待事件的线程委托中完成了真正的工作。定时器回调仅仅表示事件。工作线程无法重新进入,因为它是一个单独的线程,仅在发出事件信号时才能工作。计时器是可重入的,因为它只是发出事件的信号(看起来有点迂回,浪费,但它会起作用)
  • 只使用启动超时创建计时器,并且没有定期超时,因此它只会触发一次。计时器回调将处理该计时器对象,并在完成其工作时创建一个新对象,该工作也只会触发一次。

您可以通过使用原始计时器对象的Change()方法来管理选项#2而不处理/创建新对象,但我不确定该行为与调用{{1在第一个超时到期后使用新的启动超时。那值得一两次测试。

修改


我做了测试 - 操作计时器作为可重启的一次性似乎完美地工作,并且它比其他方法简单得多。这里有一些基于你的示例代码作为起点(一些细节可能已经改变,以便在我的机器上编译):

Change()

答案 2 :(得分:0)

我遇到过与System.Timers.Timer类似的情况,其中从线程池执行elapsed事件并且需要是可重入的。

我用这种方法解决了这个问题:

private void tmr_Elapsed(object sender, EventArgs e)
{
    tmr.Enabled = false;
    // Do Stuff
    tmr.Enabled = true;
}

根据您正在做的事情,您可能需要考虑一个System.Timers.Timer,这是MSDN

的一个很好的总结
                                         System.Windows.Forms    System.Timers         System.Threading  
Timer event runs on what thread?         UI thread               UI or worker thread   Worker thread
Instances are thread safe?               No                      Yes                   No
Familiar/intuitive object model?         Yes                     Yes                   No
Requires Windows Forms?                  Yes                     No                    No
Metronome-quality beat?                  No                      Yes*                  Yes*
Timer event supports state object?       No                      No                    Yes
Initial timer event can be scheduled?    No                      No                    Yes
Class supports inheritance?              Yes                     Yes                   No

* Depending on the availability of system resources (for example, worker threads)            

答案 3 :(得分:0)

我使用提供原子操作的Interlocked来做,并且通过CompareExchange确保一次只有一个线程进入临界区:

private int syncPoint = 0;

private void Loop()
    {
        int sync = Interlocked.CompareExchange(ref syncPoint, 1, 0);
         //ensures that only one timer set the syncPoint to  1 from 0
        if (sync == 0)
        {
            try
            {
               ...
            }
            catch (Exception pE)
            {
               ...  
            }
            syncPoint = 0;
        }

    }

答案 4 :(得分:0)

    //using Timer with callback on System.Threading namespace
    //  Timer(TimerCallback callback, object state, int dueTime, int period);
    //      TimerCallback: delegate to callback on timer lapse
    //      state: an object containig information for the callback
    //      dueTime: time delay before callback is invoked; in milliseconds; 0 immediate
    //      period: interval between invocation of callback; System.Threading.Timeout.Infinity to disable
    // EXCEPTIONS:
    //      ArgumentOutOfRangeException: negative duration or period
    //      ArgumentNullException: callback parameter is null 

    public class Program
    {
        public void Main()
        {
            var te = new TimerExample(1000, 2000, 2);
        }
    }

    public class TimerExample
    {
        public TimerExample(int delayTime, int intervalTime, int treshold)
        {
            this.DelayTime = delayTime;
            this.IntervalTime = intervalTime;
            this.Treshold = treshold;
            this.Timer = new Timer(this.TimerCallbackWorker, new StateInfo(), delayTime, intervalTime);
        }

        public int DelayTime
        {
            get;
            set;
        }

        public int IntervalTime
        {
            get;
            set;
        }

        public Timer Timer
        {
            get;
            set;
        }

        public StateInfo SI
        {
            get;
            set;
        }

        public int Treshold
        {
            get;
            private set;
        }

        public void TimerCallbackWorker(object state)
        {
            var si = state as StateInfo;

            if (si == null)
            {
                throw new ArgumentNullException("state");
            }

            si.ExecutionCounter++;

            if (si.ExecutionCounter > this.Treshold)
            {
                this.Timer.Change(Timeout.Infinite, Timeout.Infinite);
                Console.WriteLine("-Timer stop, execution reached treshold {0}", this.Treshold);
            }
            else
            {
                Console.WriteLine("{0} lapse, Time {1}", si.ExecutionCounter, si.ToString());
            }
        }

        public class StateInfo
        {
            public int ExecutionCounter
            {
                get;
                set;
            }

            public DateTime LastRun
            {
                get
                {
                    return DateTime.Now;
                }
            }

            public override string ToString()
            {
                return this.LastRun.ToString();
            }
        }
    }

    // Result:
    // 
    //  1 lapse, Time 2015-02-13 01:28:39 AM
    //  2 lapse, Time 2015-02-13 01:28:41 AM
    //  -Timer stop, execution reached treshold 2
    //