开发者

What is the expected behavior of a locally scoped Timer?

Specifically, if you create an instance of a Timer in the local scope, and then return from t开发者_开发百科hat scope:

1) Will the timer still execute?

2) When would it be garbage collected?

I offer these two scenarios:

Timer timer = new Timer(new TimerCallback((state) => { doSomething(); }));
timer.Change((int)TimeSpan.FromSeconds(30), (int)TimeSpan.FromSeconds(30));
return;

And

Timer timer = new Timer(new TimerCallback((state) => { doSomething(); }));
timer.Change((int)TimeSpan.FromSeconds(30), Timeout.Infinite);
return;


The TimerCallback has a reference to the method DoSomething(), and therefore (in your example) to this but there is no live reference going the other way so it should get collected...eventually


The timer might or might not execute, depends on whether garbage collection runs before the time executes. This is why it's good practice to keep a reference to the timer somewhere other than on the stack.

Note that this is not always problematic; for example, threads won't be collected as long as they are still running.


Here's a quick test:

class Program
{
    static void Main(string[] args)
    {
        Something something = new Something();
        Foo(something);
        Console.ReadKey(true);
        GC.Collect();
        Console.ReadKey(true);
    }

    private static void Foo(Something something)
    {
        Timer timer = new Timer(new TimerCallback(something.DoIt),null,0,5);
        return;
    }
}

public class Something
{
    public void DoIt(object state)
    {
        Console.WriteLine("foo{0}", DateTime.Now.Ticks);
    }
}

This is essentially what the compiler blows it out to (the Lambda expression in your example). When you run this, you'll notice that as long as you don't hit the first key, it'll keep putting stuff out to the console. As soon as you hit a key, and the GC kicks in, it stops. Timer still has a reference to Something, but nothing has a reference to Timer, so it's gone.


If you're talking about System.Threading.Timer, it implements IDisposable, so you should maintain a reference to it so that you can call Dispose when you're no longer using it. I don't know the answer to your particular question, but you can investigate it in a console application by running many iterations and forcing GC.Collect() to see if the Timer continues to fire. My guess is that it will eventually be collected and stop firing, unless there is some statically rooted reference created internally.

On a side note, if you want a one-time Fire-and-forget timer, you can implement one by creating a state object with a reference to the Timer, so it can Dispose itself when the timer event fires. I have a TimerService class with a WhenElapsed(TimeSpan, Action) method that uses this pattern and it's very handy for creating timeouts without having to manage the Timer instance as a field in the containing class.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜