Atomic changes System.Threading.Timer

Let's say I have an existing instance of System.Threading.Timer, and I would like to call Change on it in order to click on the firing time:

var timer = new Timer(DelayCallback, null, 10000, Timeout.Infinite); // ... (sometime later but before DelayCallback has executed) timer.Change(20000, Timeout.Infinite); 

I use this timer to perform an “idle callback” after a period of inactivity. ("Idle" and "no activity" are the conditions defined by the application in this case ... the features are not very important.) Every time I perform an "action", I want to reset the timer so that it always fires 10 seconds after of this.

However, there is an inherent condition for the race, because when I call Change, I can’t say whether Timer has already been launched based on its old settings. (I can, of course, say whether my callback has occurred, but I cannot say whether the CLR internal timer thread has called my callback in threadpool, and its execution is inevitable.)

Now I know that I can call Dispose on the timer instance and recreate it every time I need to push it. but it seems less effective than just modifying an existing timer. Of course, this may not be so ... I ran a few micro tests a bit and let you know.

Alternatively, I can always track the expected firing time (via DateTime.Now.AddSeconds (10)), and if the original timer fires, ignore it by checking DateTime.Now in the callback. (I have a suspicious concern that this may not be 100% reliable due to a timer using TimeSpan and my checking using DateTime ... this may not be a problem, but I'm not quite comfortable with it for some reason reason ...)

My questions:

  • Is there a good way to call Timer.Change and find out if I managed to change it before the callback was queued on threadpool? (I don’t think so, but it’s not painful to ask ...)
  • Has anyone else implemented (what I call) a "pushing timer" like this? If so, I would like to hear how you solved the problem.

This question is somewhat hypothetical in nature, since I already have several working solutions (based on Dispose and based on DateTime.Now) ... I am mainly interested in rumors about offers related to performance (since I will “drop” the timer Often).

Thanks!

+6
c #
source share
3 answers

it looks like what you really want is an event without an application

 System.Windows.Forms.Application.Idle 
+1
source share

Im interprets your questions as a request to implement the IdleNotifier interface specified below. You also indicate that ActionOccured () should be fast.

 public delegate void IdleCallback(); public interface IdleNotifier { // Called by threadpool when more than IdleTimeSpanBeforeCallback // has passed since last call on ActionOccured. IdleCallback Callback { set; } TimeSpan IdleTimeSpanBeforeCallback { set; } void ActionOccured(); } 

I provide an implementation using System.Threading.Timer below. Important points in the implementation:

  • We accept that the timer can wake up at any time and make sure everything is in order.
  • Since we assume that the timer wakes up relatively rarely, we can do expensive work during these times.
  • Since we can do all the logic in the timer callback, all we need to do is to “press the timer” in order to remember when we last pressed it.

Implementation:

 public class IdleNotifierTimerImplementation : IdleNotifier { private readonly object SyncRoot = new object(); private readonly Timer m_Timer; private IdleCallback m_IdleCallback = null; private TimeSpan m_IdleTimeSpanBeforeEvent = TimeSpan.Zero; // Null means there has been no action since last idle notification. private DateTime? m_LastActionTime = null; public IdleNotifierTimerImplementation() { m_Timer = new Timer(OnTimer); } private void OnTimer(object unusedState) { lock (SyncRoot) { if (m_LastActionTime == null) { m_Timer.Change(m_IdleTimeSpanBeforeEvent, TimeSpan.Zero); return; } TimeSpan timeSinceLastUpdate = DateTime.UtcNow - m_LastActionTime.Value; if (timeSinceLastUpdate > TimeSpan.Zero) { // We are no idle yet. m_Timer.Change(timeSinceLastUpdate, TimeSpan.Zero); return; } m_LastActionTime = null; m_Timer.Change(m_IdleTimeSpanBeforeEvent, TimeSpan.Zero); } if (m_IdleCallback != null) { m_IdleCallback(); } } // IdleNotifier implementation below public void ActionOccured() { lock (SyncRoot) { m_LastActionTime = DateTime.UtcNow; } } public IdleCallback Callback { set { lock (SyncRoot) { m_IdleCallback = value; } } } public TimeSpan IdleTimeSpanBeforeCallback { set { lock (SyncRoot) { m_IdleTimeSpanBeforeEvent = value; // Run OnTimer immediately m_Timer.Change(TimeSpan.Zero, TimeSpan.Zero); } } } } 

There are many direct performance improvements in this code.

If someone is interested in my thoughts, just ask me.

+1
source share

I really needed to create my own Timing class for the MMORPG that I created. He could track over 100,000 "entities" that had timers for processing AI and other tasks. Based on the various actions that could be taken, I would have to temporarily delay the event.

Now my time class has been completely written by hand, so it will not be exactly what you are looking for. But something you could do would be similar to the solution I came up with, to do something like:

 while (sleepyTime > 0) { int temp = sleepyTime; sleepyTime = 0; Thread.Sleep(temp); } // here where your actual code is. 

Then you can create a “Delay” method, which mainly applies only to sleepyTime ads.

0
source share

All Articles