Когда переменные метода, доступные в анонимном методе, собираются мусором? - PullRequest
0 голосов
/ 09 февраля 2011

Например, необходимо ли добавлять экземпляр Timer в список, как я делаю здесь, чтобы предотвратить сбор мусора Timer? Обычно, если обратный вызов не был анонимным, aswer - да, но так каканонимно ли я представляю, что переменные в блоке метода, которые доступны в блоке анонимного метода, будут собираться только после завершения анонимного метода?В этом случае нет необходимости сохранять ссылку, как я делаю ..:

private static List<Timer> timers = new List<Timer>();

public static void RunCallbackAfter(Action callback, int secondsToWait)
{
        Timer t = null;
        t = new Timer(new TimerCallback(delegate(object state)
            {
                SomeThread.BeginInvoke(callback);
                timers.Remove(t);
            }), null, secondsToWait*1000, Timeout.Infinite);
        timers.Add(t);
}

Ответы [ 3 ]

2 голосов
/ 09 февраля 2011

Объекты, на которые ссылаются захваченные переменные в анонимном методе, не будут иметь права на сборку мусора, пока делегат, созданный анонимным методом, не будет иметь права на сборку мусора.

Однако, если только Timer имеет ссылку на делегат, а ничто иное не имеет ссылки на таймер, то я подозреваю, что оба будут иметь право на сборку мусора, при условии действительно таймер, на который нужно ссылаться. (Кажется, я помню, что некоторые таймеры делают требуют этого, а некоторые не . Я не могу вспомнить, какой именно.)

Кроме того, если вы удалили вызов timers.Remove(t) из анонимного метода, он не будет захватывать t. Это только перехваченные переменные с увеличенным временем жизни ... не каждая переменная в методе, который содержит анонимный метод.

0 голосов
/ 09 февраля 2011

Как правило, анонимные функции в C # сохраняют любые локальные переменные , на которые они ссылаются , до тех пор, пока сама функция anon не будет уничтожена. Естественно, удаление вызова удаления в этом случае приведет к удалению ссылки, то есть переменная больше не будет поддерживаться обратным вызовом.

Однако это здесь образует круговую ссылку; если нет внешней ссылки, таймер и обратный вызов могут быть уничтожены одновременно. Я не уверен, считается ли запуск таймера внешней ссылкой, поддерживающей его в C #, поэтому я не могу полностью ответить на ваш вопрос. Если запущенный таймер обрабатывается как имеющий внешнюю ссылку, он сам по себе будет поддерживать как таймер, так и обратный вызов.

0 голосов
/ 09 февраля 2011

Нет.Вам не нужно прятать свой таймер.Обычно это сборщик мусора, поскольку на него ссылается только ваш делегат.Тем не менее, я считаю, что конструктор Timer помещает ссылку в базовую среду выполнения, поэтому с вами все будет в порядке.

Эрик Липперт, вероятно, что-то скажет в своем блоге: Реализация анонимных методовв C # и его последствиях (часть 1)

Ваша Timer переменная t будет оставаться доступной, пока есть ссылка на ваш анонимный метод.На ваш анонимный метод будут ссылаться до тех пор, пока не сработает событие.По крайней мере, так долго.

Согласно Эрику Липперту, компилятор c # превращает ваш код во что-то другое, причем контекст вашего метода (включая указатель this включающего объекта) весь в своем собственноммаленький сгенерированный компилятором класс.Так что, похоже, сам анонимный метод (или делегат) содержит ссылку на таймер.

О, и все остальные в этом потоке правы: я просто выполнил некоторые вещи (и узнал о том, как обрабатывает компилятор C #)анонимные методы одновременно).

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

Используя Reflector, вы можете следовать по следу от System.Timer.Timer() до TimerBase, который имеет метод extern AddTimerNative.Я не уверен, как заглянуть в это, но я держу пари, что он регистрирует ваш таймер в ОС.

Вывод: Ваш таймер не выйдет из области видимости, так какна него будет ссылаться ОС, пока он не сработает.

...