Каково ожидаемое поведение локального таймера? - PullRequest
9 голосов
/ 27 мая 2011

В частности, если вы создаете экземпляр Timer в локальной области, а затем возвращаетесь из этой области:

1) Таймер все еще будет работать?

2) Когда это будет сбор мусора?

Я предлагаю эти два сценария:

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

А

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

Ответы [ 4 ]

6 голосов
/ 27 мая 2011

У TimerCallback есть ссылка на метод DoSomething(), и поэтому (в вашем примере) на this, но нет живой ссылки, идущей в другую сторону, поэтому она должна быть собрана ... в конечном итоге

3 голосов
/ 27 мая 2011

Вот краткий тест:

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);
    }
}

По сути, это то, к чему это приводит компилятор (лямбда-выражение в вашем примере).Когда вы запустите это, вы заметите, что до тех пор, пока вы не нажмете первую клавишу, она будет продолжать выводить информацию на консоль.Как только вы нажмете клавишу, и GC начнет действовать, он остановится.Timer по-прежнему имеет ссылку на Something, но ничто не имеет ссылки на таймер, поэтому он исчез.

3 голосов
/ 27 мая 2011

Таймер может работать или не выполняться, в зависимости от того, выполняется ли сборка мусора раньше времени. Вот почему рекомендуется хранить ссылку на таймер где-то, кроме стека.

Обратите внимание, что это не всегда проблематично; например, потоки не будут собираться, пока они еще работают.

0 голосов
/ 27 мая 2011

Если вы говорите о System.Threading.Timer, он реализует IDisposable, поэтому вам следует сохранить ссылку на него, чтобы вы могли вызывать Dispose, когда вы его больше не используете.Я не знаю ответа на ваш конкретный вопрос, но вы можете исследовать его в консольном приложении, выполнив много итераций и заставив GC.Collect() проверить, продолжает ли срабатывать таймер.Я предполагаю, что в конечном итоге он будет собран и прекратит срабатывание, если только внутри не будет создана какая-то статически укорененная ссылка.

В примечании, если вам нужен одноразовый таймер «Забей и забудь», вы можетереализовать его, создав объект состояния со ссылкой на таймер, чтобы он мог утилизировать себя при возникновении события таймера.У меня есть класс TimerService с методом WhenElapsed(TimeSpan, Action), который использует этот шаблон, и он очень удобен для создания тайм-аутов без необходимости управлять экземпляром Timer как полем в содержащем классе.

...