12

I'm using Threading.Timer, like:

new Timer(new TimerCallback(y=>
{
    try
    {
        Save(Read(DateTime.Now));
        // here i want to dispose this timer
    }
    catch
    {                          
    }
}),null,100000,10000);

How can I dispose this timer inside of a callback. or workaround? Update: Let me explain the situation. I want to try to call the method "Save", while it throws an exception. If it works, I need to stop the timer.

eba
  • 959
  • 2
  • 11
  • 14

5 Answers5

17

Try this:

Timer timer = null;
timer = new Timer(new TimerCallback(y =>
{    
    try    
    {
        Save(Read(DateTime.Now));        
        // here i want to dispose this timer
        timer.Dispose();    
    }    
    catch    
    {    
    }
}));
timer.Change(10000, 10000);

EDIT:

I changed the above code slightly according to Chuu's suggestion. Note that if the TimerCallback is called simultanuously by different timer events, Timer.Dispose may end up being called several times. Luckily the Timer does not care if it is being disposed of several times.

Jakob Christensen
  • 14,826
  • 2
  • 51
  • 81
  • Wow, here I am trying to make this question complicated. The obvious solution is indeed a `Dispose` statement. I assumed this was already excluded as "not an option" for whatever reason. – Cody Gray - on strike Mar 08 '11 at 11:16
  • I guess it was too obvious :-) – Jakob Christensen Mar 08 '11 at 11:32
  • My loss, your gain. I often try to make things too complicated. +1 for the correct, yet simple, solution. – Cody Gray - on strike Mar 09 '11 at 05:26
  • 3
    I don't think this is 100% thread safe, the following (ridiculously unlikely) series of events could theoretically happen: 1. Timer created. 2. 3. Callback fire on Threadpool 4. 5. Assignment occurs. The thread-safe way would probably be to create the timer with infinite timeouts to force the assignment before the threadpool callback, then use Change() to set the recurring properties. – Chuu Jun 16 '11 at 14:55
  • @Chuu: Good point. I think you are right. I changed my answer accordingly. – Jakob Christensen Jun 16 '11 at 16:42
  • @JakobChristensen Resharper gives the following warning "Access to modified closure"? Safe to ignore the warning in this case? – redandblue Jul 05 '17 at 23:13
  • @user2383728 Resharper is probably complaining that `timer` is being modified outside of the delegate after we created a closure around `timer`. You can ignore the warning in this case. – Jakob Christensen Jul 06 '17 at 16:13
4

Here's a better way to do this. When you use the constructor with only one param (TimerCallback), the state passed to the callback will be the timer itself.

Timer timer = new Timer(o =>
{
     ((Timer)o).Dispose();
     //Your code here     
});

//Start the timer
timer.Change(100000,10000);

Here is an example from the msdn docs :

public void StartTimer(int dueTime)
{
    Timer t = new Timer(new TimerCallback(TimerProc));
    t.Change(dueTime, 0);
}

private void TimerProc(object state)
{
    // The state object is the Timer object.
    Timer t = (Timer) state;
    t.Dispose();
    Console.WriteLine("The timer callback executes.");
}

http://msdn.microsoft.com/en-us/library/ms149618(v=vs.110).aspx

cslecours
  • 164
  • 8
3

You need to keep the reference of the timer in a variable -

public class MyClass
{
    private Timer _timer;

    public void StartTimer()
    {
        _timer =  new Timer(new TimerCallback(y=>{
                            try
                            {
                                Save(Read(DateTime.Now));
                                _timer.Dispose();
                            }
                            catch { 

                            }
                        }),null,100000,10000);
    }



}

Note: This is untested code. Please check if it works and update.

Unmesh Kondolikar
  • 9,256
  • 4
  • 38
  • 51
0

Take care if you use multithreading or multitasking! if so, here you're the code and a solucion for a CancelAfter method extensor (.net 4.0):

       private static object syncObj = new object();

       public static void CancelAfter(this CancellationTokenSource source, int timeoutMilliseconds, Action code = null)
    {
        if (timeoutMilliseconds == 0) return; // No timeout

        if (source == null)
        {
            throw new NullReferenceException();
        }
        if (timeoutMilliseconds < -1)
        {
            throw new ArgumentOutOfRangeException("timeout");
        }
        Timer timer = new Timer(delegate(object self)
        {
            lock (syncObj)
            {
                try
                {
                    if (null != code)
                        code.Invoke();
                    source.Cancel();

                    ((IDisposable)self).Dispose();
                }
                catch (ObjectDisposedException)
                {
                }                   
            }
        });
        timer.Change(timeoutMilliseconds, -1);
    }
}

Regards, Juanlu, ElGuerre

0

You'll have to store a reference to the timer somewhere and pass that in as state to the timer object itself. Try creating a class something like this:

public class TimerContainer
{
  public Timer Timer { get; set; }
}

Then use it in your method like so:

Action<object> tcb = state =>
{
  var container = (TimerConatiner)state;
  try
  {
    Save(Read(DateTime.Now));
    container.Timer.Dispose();
  }
  catch
  {
    // whatever...
  }
};

var container = new TimerContainer();
container.Timer = new Timer(tcb, container, 100000, 10000);
OJ.
  • 28,944
  • 5
  • 56
  • 71