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
>
>