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

Kor*_*tak 16 c# timer callback 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);
} 
Run Code Online (Sandbox Code Playgroud)

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

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

jsw*_*jsw 46

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

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


Mic*_*urr 6

几种可能的解决方案:

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

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

编辑:


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

private Timer _creatorTimer;

// BackgroundWorker's work
private void CreatorWork(object sender, EventArgs e) {
    // note: there's only a start timeout, and no repeat timeout
    //   so this will fire only once
    _creatorTimer = new Timer(CreatorLoop, null, 1000, Timeout.Infinite);

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

private void CreatorLoop(object state) {
    Console.WriteLine( "In CreatorLoop...");
    /*
        ... Work here
    */
    Thread.Sleep( 3000);

    // Reenable timer
    Console.WriteLine( "Exiting...");

    // now we reset the timer's start time, so it'll fire again
    //   there's no chance of reentrancy, except for actually
    //   exiting the method (and there's no danger even if that
    //   happens because it's safe at this point).
    _creatorTimer.Change(1000, Timeout.Infinite);
}
Run Code Online (Sandbox Code Playgroud)