10

How can I stop System.Threading.Timer in it's call back method. I referenced MSDN, but couldn't find anything useful. Please help.

Anatoliy Nikolaev
  • 22,370
  • 15
  • 69
  • 68
Proceso
  • 567
  • 1
  • 6
  • 24
  • See also http://stackoverflow.com/questions/6379541/reliably-stop-system-threading-timer – lc. Aug 13 '12 at 16:37
  • Does this answer your question? [Reliably stop System.Threading.Timer?](https://stackoverflow.com/questions/6379541/reliably-stop-system-threading-timer) – Michael Freidgeim Nov 21 '20 at 22:39

6 Answers6

16

First, the callback method must have the timer instance in-scope.

Then the simple incantation

timerInstance.Change( Timeout.Infinite , Timeout.Infinite ) ;

will shut down the timer. It is possible that the timer might invoke the callback method once more after the change, I believe, depending on the state it's in.

Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135
  • It's better to dispose it because that captures the semantic meaning better. It might also be faster. – usr Aug 13 '12 at 16:46
  • 5
    Depends on whether the timer is to be restarted later or you're done with it. The OP specified that she wanted to "stop the timer", not "destroy the timer". The timer instance and its lifetime is the responsibility of its creator. – Nicholas Carey Aug 13 '12 at 16:50
  • yap with that solution that's the problem, thread doesn't stop immediately. – Proceso Aug 13 '12 at 18:13
4
timer.Change(Timeout.Infinite, Timeout.Infinite);
Volma
  • 1,305
  • 9
  • 17
2

Try this:

If you want you could let timer continue firing the callback method and include the code below

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

check out this link too:

Stopping timer in its callback method

Community
  • 1
  • 1
LOZ
  • 1,169
  • 2
  • 16
  • 43
1

You can simply call myTimer.Change(Timeout.Infinite, Timeout.Infinite).

Technically, only the first parameter (dueTime) needs to be specified as Timeout.Infinite for the timer to stop.

For more information, see Timer.Change Method.

Jon Senchyna
  • 7,867
  • 2
  • 26
  • 46
  • It's better to dispose it because that captures the semantic meaning better. It might also be faster. – usr Aug 13 '12 at 16:47
  • It depends on if the user is permanently stopping the timer, or if they're just pausing it for a time and possibly restarting it at a later time. If you use `.Dispose()` to simply pause the timer, you will run into issues if you want to either restart it, or simply change the dueTime or period. In those cases, it's safer (and easier) to just use `timer.Change()`. – Jon Senchyna Aug 13 '12 at 17:19
1

I found out the hard way that Change(Timeout.Infinite, Timeout.Infinite) isn't quite reliable, and switched over to System.Timers.Timer with AutoReset = false.

Mark Sowul
  • 10,244
  • 1
  • 45
  • 51
1

The problem with Timer is that it might be called after disposing its owner class. The following implementation worked for me by using the state object of the Timer initializer. Heap will not remove that object until it is consumed. This was my only way to gracefully cleanup timer callback.

using System;
using System.Threading;

namespace TimerDispose
{
    /// <summary>
    /// A timer-containing class that can be disposed safely by allowing the timer 
    /// callback that it must exit/cancel its processes
    /// </summary>
    class TimerOwner : IDisposable
    {
        const int dueTime = 5 * 100;       //halve a second
        const int timerPeriod = 1 * 1000;   //Repeat timer every one second (make it Timeout.Inifinite if no repeating required)

        private TimerCanceller timerCanceller = new TimerCanceller();

        private Timer timer;

        public TimerOwner()
        {
            timerInit(dueTime);
        }

        byte[] dummy = new byte[100000];

        /// <summary>
        /// 
        /// </summary>
        /// <param name="dueTime">Pass dueTime for the first time, then TimerPeriod will be passed automatically</param>
        private void timerInit(int dueTime)
        {

            timer = new Timer(timerCallback,
                timerCanceller,     //this is the trick, it will be kept in the heap until it is consumed by the callback 
                dueTime,
                Timeout.Infinite
            );

        }

        private void timerCallback(object state)
        {
            try
            {
                //First exit if the timer was stoped before calling callback. This info is saved in state
                var canceller = (TimerCanceller)state;
                if (canceller.Cancelled)
                {
                    return; //
                }

                //Your logic goes here. Please take care ! the callback might have already been called before stoping the timer
                //and we might be already here after intending of stoping the timer. In most cases it is fine but try not to consume
                //an object of this class because it might be already disposed. If you have to do that, hopefully it will be catched by
                //the ObjectDisposedException below




                dummy[1] = 50;  //just messing up with the object after it might be disposed/nulled

                //Yes, we need to check again. Read above note
                if (canceller.Cancelled)
                {
                    //Dispose any resource that might have been initialized above
                    return; //
                }

                if (timerPeriod != Timeout.Infinite)
                {
                    timerInit(timerPeriod);
                }
            }
            catch (ObjectDisposedException ex)
            {
                Console.WriteLine("A disposed object accessed");
            }
            catch (NullReferenceException ex)
            {
                Console.WriteLine("A nulled object accessed");
            }
            catch (Exception ex)
            {

            }
        }

        public void releaseTimer()
        {
            timerCanceller.Cancelled = true;
            timer.Change(Timeout.Infinite, Timeout.Infinite);
            timer.Dispose();
        }

        public void Dispose()
        {
            releaseTimer();
            dummy = null;   //for testing
            GC.SuppressFinalize(this);
        }
    }

    class TimerCanceller
    {
        public bool Cancelled = false;
    }


    /// <summary>
    /// Testing the implementation
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {
            var list = new System.Collections.Generic.List<TimerOwner>();
            Console.WriteLine("Started initializing");
            for (int i = 0; i < 500000; i++)
            {
                list.Add(new TimerOwner());
            }

            Console.WriteLine("Started releasing");
            foreach (var item in list)
            {
                item.Dispose();
            }

            Console.WriteLine("Press any key to exit");
            Console.ReadKey();
        }
    }
}
Ibrahim
  • 183
  • 1
  • 6