Question

I'm coping with a problem. In fact I need to use different timers in my programe and Timers from Framework .Net don't do what I expect. So I decided to create my own Timer, but my Timer uses too much CPU. This is my code :

using System;
using System.Threading;

namespace XXXXXXX.Common.Types
{
public delegate void TimerFinishedEventHandler(object sender, EventArgs e);

class Timer
{
    #region Attributes
    private long m_InitialTickCount;
    private long m_Interval;
    private Thread m_Thread;
    private bool m_Enabled;
    #endregion

    #region Events
    public event TimerFinishedEventHandler Finished;
    #endregion

    #region Constructors
    public Timer(long interval, TimerFinishedEventHandler e)
    {
        Finished += e;
        m_Interval = interval;
        Start(m_Interval);
    }
    #endregion

    #region Public methods
    /// <summary>
    /// Start the timer thread.
    /// </summary>
    public void Start(long interval)
    {
        m_Interval = interval;
        m_Enabled = true;
        m_InitialTickCount = Environment.TickCount;

        if (m_Thread == null)
        {
            m_Thread = new Thread(Check);
            m_Thread.Start();
        }
    }

    /// <summary>
    /// Stop the Timer.
    /// </summary>
    public void Stop()
    {
        m_Enabled = false;
    }

    /// <summary>
    /// Restart the Timer.
    /// </summary>
    public void Restart()
    {
        m_InitialTickCount = Environment.TickCount;
    }
    #endregion

    #region Private methods
    /// <summary>
    /// Check if the timer is finished or not.
    /// </summary>
    private void Check()
    {
        while (true)
        {
            if (!m_Enabled)
                return;

            if (Environment.TickCount > m_InitialTickCount + m_Interval)
            {
                OnFinished(EventArgs.Empty);
                return;
            }
        }
    }

    /// <summary>
    /// Called when the Timer is Finished.
    /// </summary>
    /// <param name="e">Event</param>
    protected virtual void OnFinished(EventArgs e)
    {
        if (Finished != null)
            Finished(this, e);
    }
    #endregion
}
}

Is there anybody who have a solution ? Because when I launch my program, 2 or 3 Timers are created, another thread run and I have my CPU 100% using.

Was it helpful?

Solution

There is absolutely no reason you can't have multiple timers. I have programs that have hundreds of timers, and at any time a handful of them can be actually doing work. The point of timers is that they allow you to schedule periodic actions and not consume any CPU resources except when the actions are actually being processed. That is, if you set a timer to tick once per minute, then that timer doesn't occupy a thread, doesn't consume any memory (beyond a token amount for the timer handle and the callback address), and doesn't consume any CPU resources. Only when the timer "ticks" once per minute is a thread allocated to execute code for it. And typically that is a pool thread that already exists, so thread startup time is negligible.

Using a timer is very easy: you create a method for the timer to execute, and you schedule the timer to run it. For example:

System.Threading.Timer myTimer = 
    new System.Threading.Timer(MyTimerProc, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));

void MyTimerProc(object state)
{
    // do something here
}

You could have another timer that ticks every 30 seconds and executes a different timer proc:

System.Threading.Timer myOtherTimer = 
    new System.Threading.Timer(MyOtherTimerProc, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));

void MyOtherTimerProc(object state)
{
    // do something else here
}

The timers won't interfere with each other. Of course, if code in the timer procs modifies shared data (for example, both procs try to update a list or a dictionary), then you'll have to synchronize access to that shared data or use a concurrent data structure.

You can get into trouble with reentrancy if the processing in your timer proc takes longer than the timer period. If the processing in MyTimerProc takes longer than 60 seconds, then another timer tick can come along and now you have two threads executing that timer proc. That can cause many different types of problems if your code is not set up to handle it. Typically you eliminate that problem by making the timer a one-shot and restarting it at the end of each processing cycle. There are examples of doing that here on Stack Overflow.

System.Timers.Timer is a component wrapper around System.Threading.Timer. The idea that it's "optimized for high performance threading" or some such is silly. System.Timers.Timer gives you a familiar event-oriented interface, and also provides a SynchronizingObject, which lets you have the event raised on a particular thread rather than having to explicitly Invoke as you would with System.Threading.Timer. Typically, that's only useful in UI applications.

System.Timers.Timer has one particularly ugly "feature" that I consider a bug: it squashes exceptions. As the documentation says:

In the .NET Framework version 2.0 and earlier, the Timer component catches and suppresses all exceptions thrown by event handlers for the Elapsed event.

That behavior still exists in .NET 4.5. The problem is that if you have your Elapsed event:

private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
    // do stuff here
}

If your event handler throws an exception, it will propagate back to the timer code, which squashes the exception and never tells you about it. In effect, the timer does this:

try
{
    OnTimedEvent(source, args);
}
catch
{
    // Squash those pesky exceptions. Who needs them?
}

It's a bug hider because you never know that the exception was thrown. So your program doesn't work and you can't figure out why. It's for this reason that I strongly recommend that you NOT use System.Timers.Timer. Use System.Threading.Timer instead; after all, it's the base that System.Timers.Timer is built on.

OTHER TIPS

To answer your question directly, the reason your CPU usage is so high is that you are using a tight while loop to check your elapsed event. This is sometimes called a spin lock (called so because it is one, very inefficient, way to implement a semaphore where the thread checks a locking variable in a tight loop, thus it "spins").

Instead of a tight loop, you need to block and allow something else to run for a while:

private void Check()
{
    while (true)
    {
        if (!m_Enabled)
            return;

        Thread.Sleep(10); //10 millisecond resolution for this timer
        if (Environment.TickCount > m_InitialTickCount + m_Interval)
        {
            OnFinished(EventArgs.Empty);
            return;
        }
    }
}

The resolution depends on how long you sleep for. That being said, the provided timers should always be sufficent, even using 2 System.Threading.Timers would work. I have personally used multiples of both System.Threading.Timer and System.Timers.Timer with no problems.

Of course, with all these timers, you need to be careful about accessing shared resources (perhaps what you mean by the existing timers blocking other threads?). Deadlock is a very real scenario in multi-threading, but doesn't have much to do with timers.

For @Jim Mischel.

In my class connecting to my website, checking datas :

        #region Attributes
        private static Timer m_TimerNextCheck;
        #endregion

        #region Méthodes publiques
        public static void StartCheck()
        {
            Thread licenceThread = new Thread(Checking);
            licenceThread.Start();
        }
        #endregion

        #region Méthodes privées
        private static void Checking()
        {
            //connect to the website

            try
            {
                HttpWebResponse httpWebResponse = (HttpWebResponse) request.GetResponse();

                StreamReader streamReader = new StreamReader(httpWebResponse.GetResponseStream(), Encoding.Default);

                string response = streamReader.ReadToEnd();

                httpWebResponse.Close();

                if (//Some code)
                {
                    //Some code
                }
                else
                {

                    if (m_TimerNextCheck == null)
                        m_TimerNextCheck = new Timer(TimerNextCheckFinished, null, 300000, Timeout.Infinite);
                    else
                        m_TimerNextCheck.Change(300000, Timeout.Infinite);

                }
            }
            catch (WebException exception)
            {
                //Some code

                if (m_TimerNextCheck == null)
                    m_TimerNextCheck = new Timer(TimerNextCheckFinished, null, 60000, Timeout.Infinite);
                else
                    m_TimerNextCheck.Change(60000, Timeout.Infinite);
            }
        }

        private static void TimerNextCheckFinished(object statusInfos)
        {
            Checking();
        }
        #endregion

In another class :

    #region Attributs
    private Thread m_ConnectionThread;
    private Timer m_TimerConnectionThread;
    #endregion

    #region Méthodes publiques
    public void Launch()
    {
        m_ConnectionThread = new Thread(Connect);
        m_ConnectionThread.Start();
    }

    public void GetNextMeal()
    {
        //Some code

        if (//Some code)
        {
            //Some code

            if (m_TimerConnectionThread == null)
                m_TimerConnectionThread = new Timer(TimerConnectionThreadFinished, null, 
                    (int)TimeSpan.FromHours(difference.Hour).TotalMilliseconds + 
                    (int)TimeSpan.FromMinutes(difference.Minute).TotalMilliseconds, Timeout.Infinite);
            else
                m_TimerConnectionThread.Change((int)TimeSpan.FromHours(difference.Hour).TotalMilliseconds + 
                    (int)TimeSpan.FromMinutes(difference.Minute).TotalMilliseconds, Timeout.Infinite);
        }
        else
        {
            //Some code
        }
    }

    public void TryReconnect(int minute)
    {
        //Some code

        if (m_TimerConnectionThread == null)
            m_TimerConnectionThread = new Timer(TimerConnectionThreadFinished, null, (int)TimeSpan.FromMinutes(minute).TotalMilliseconds,
                Timeout.Infinite);
        else
            m_TimerConnectionThread.Change((int)TimeSpan.FromMinutes(minute).TotalMilliseconds, Timeout.Infinite);

        //Some code
    }

    //Some code
    #endregion

    #region Méthodes privées
    private void Connect()
    {
        if (m_TimerConnectionThread != null)
            m_TimerConnectionThread.Change(Timeout.Infinite, Timeout.Infinite);

        //Some code
    }

    //Some code

    private void TimerConnectionThreadFinished(object stateInfo)
    {
        Connect();
    }
    #endregion

And it works good !

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top