Salakhetdinov Shamil
mcp2004 at mail.ru
Tue May 15 12:48:01 CDT 2012
Hi John -- Is it correct understanding that you have a set of tasks to run periodically, fired by specified timer intervals, and when one task is running all the others are blocked? Thank you. -- Shamil Tue, 15 May 2012 10:28:48 -0400 от jwcolby <jwcolby at colbyconsulting.com>: > Is anyone using timers to periodically run processes? > > I am (was hopefully?) having problems with apparent deadlocks and I found this on the MS site: > > http://msdn.microsoft.com/en-us/library/system.timers.timer.stop.aspx > > which discusses deadlocks and provides a solution. I took the code and wrapped it in clsTimer > making it fully instantiable so that I could use it in many places. > > The problem of course is that the code running in the timer's thread still has to cleanly return but > with luck at least this (deadlock) part is handled. I am sharing my class so that anyone else doing > this stuff can look it over, comment and hopefully find any bugs I might have created in implementation. > > Here is my version of the indicated code. Comments welcome. > > using System; > using System.Timers; > using System.Threading; > using System.Windows.Forms; > namespace projAccuzip2 > { > class clsTimer : IDisposable > { > > #region Header > // Timer. > private bool timerBusy = false; > private System.Timers.Timer cTimer = new System.Timers.Timer(); > > // This is the synchronization point that prevents events > // from running concurrently, and prevents the main thread > // from executing code after the Stop method until any > // event handlers are done executing. > private int syncPoint = 0; > > // Count the number of times the event handler is called, > // is executed, is skipped, or is called after Stop. > private int numEvents = 0; > private int numExecuted = 0; > private int numSkipped = 0; > private int numLate = 0; > > // Count the number of times the thread that calls Stop > // has to wait for an Elapsed event to finish. > private int numWaits = 0; > > public clsTimer() > { > cTimer.Elapsed += new System.Timers.ElapsedEventHandler(cTimer_Elapsed); > } > public clsTimer(int TimerInterval) > { > cTimer.Interval = TimerInterval; > cTimer.Elapsed += new System.Timers.ElapsedEventHandler(cTimer_Elapsed); > } > #endregion > > #region Properties > public int pNumEvents { get { return numEvents; } } > public int pNumExecuted { get { return numExecuted; } } > public int pNumSkipped { get { return numSkipped; } } > public int pNumLate { get { return numLate; } } > public bool pTimerBusy { get { return timerBusy; } } > public int pTimerInterval { set { cTimer.Interval = value; } } > #endregion > > #region Events > > public delegate void delTimer(); > public event delTimer evTimer; > private void cTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) > { > numEvents += 1; > > // This example assumes that overlapping events can be > // discarded. That is, if an Elapsed event is raised before > // the previous event is finished processing, the second > // event is ignored. > // > // CompareExchange is used to take control of syncPoint, > // and to determine whether the attempt was successful. > // CompareExchange attempts to put 1 into syncPoint, but > // only if the current value of syncPoint is zero > // (specified by the third parameter). If another thread > // has set syncPoint to 1, or if the control thread has > // set syncPoint to -1, the current event is skipped. > // (Normally it would not be necessary to use a local > // variable for the return value. A local variable is > // used here to determine the reason the event was > // skipped.) > // > int sync = Interlocked.CompareExchange(ref syncPoint, 1, 0); > if (sync == 0) > { > timerBusy = true; > // > numExecuted += 1; > > // No other event was executing. > // The event handler now raises an event out in code > evTimer(); > > // Release control of syncPoint. > syncPoint = 0; > timerBusy = false; > } > else > { > if (sync == 1) { numSkipped += 1; } else { numLate += 1; } > } > } > #endregion > > #region Methods > public void mTimerStart() > { > syncPoint = 0; > cTimer.Enabled = true; > } > public void mTimerStop() > { > // Allow the timer to run for a period of time, and then > // stop it. > cTimer.Stop(); > > // The 'counted' flag ensures that if this thread has > // to wait for an event to finish, the wait only gets > // counted once. > bool counted = false; > > // Ensure that if an event is currently executing, > // no further processing is done on this thread until > // the event handler is finished. This is accomplished > // by using CompareExchange to place -1 in syncPoint, > // but only if syncPoint is currently zero (specified > // by the third parameter of CompareExchange). > // CompareExchange returns the original value that was > // in syncPoint. If it was not zero, then there's an > // event handler running, and it is necessary to try > // again. > while (Interlocked.CompareExchange(ref syncPoint, -1, 0) != 0) > { > Application.DoEvents(); //Let Windows threads process > > // Give up the rest of this thread's current time > // slice. This is a naive algorithm for yielding. > Thread.Sleep(1); > > // Tally a wait, but don't count multiple calls to > // Thread.Sleep. > if (!counted) > { > numWaits += 1; > counted = true; > } > } > > // Any processing done after this point does not conflict > // with timer events. This is the purpose of the call to > // CompareExchange. If the processing done here would not > // cause a problem when run concurrently with timer events, > // then there is no need for the extra synchronization. > } > #endregion > > #region Dispose > private bool disposed = false; //Track whether Dispose has been called. > ~clsTimer() > { > Dispose(true); > } > public void Close() > { > Dispose(true); > // This object will be cleaned up by the Dispose method. > // Therefore, you should call GC.SupressFinalize to > // take this object off the finalization queue > // and prevent finalization code for this object > // from executing a second time. > GC.SuppressFinalize(this); > } > // Implement IDisposable. > // Do not make this method virtual. > // A derived class should not be able to override this method. > public void Dispose() > { > Dispose(true); > // This object will be cleaned up by the Dispose method. > // Therefore, you should call GC.SupressFinalize to > // take this object off the finalization queue > // and prevent finalization code for this object > // from executing a second time. > GC.SuppressFinalize(this); > } > // Dispose(bool disposing) executes in two distinct scenarios. > // If disposing equals true, the method has been called directly > // or indirectly by a user's code. Managed and unmanaged resources > // can be disposed. > // If disposing equals false, the method has been called by the > // runtime from inside the finalizer and you should not reference > // other objects. Only unmanaged resources can be disposed. > private void Dispose(bool disposing) > { > if (!this.disposed) // Check to see if Dispose has already been called. > { > if (disposing) // If disposing equals true, dispose all managed and unmanaged > resources. > { > mTimerStop(); > cTimer = null; > } > } > disposed = true; > } > #endregion > > } > } > > > > -- > John W. Colby > Colby Consulting > > Reality is what refuses to go away > when you do not believe in it > > _______________________________________________ > dba-VB mailing list > dba-VB at databaseadvisors.com > http://databaseadvisors.com/mailman/listinfo/dba-vb > http://www.databaseadvisors.com > >