Не имеющие ссылки активный DispatcherTimer и сборщик мусора

1

У меня есть следующий XAML:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:self="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Label Name="lblTime" FontSize="48" HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
</Window>

Код:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DispatcherTimer timer = new DispatcherTimer();
        timer.Interval = TimeSpan.FromSeconds(1);
        timer.Tick += timer_Tick;
        timer.Start();
        GC.Collect();
    }
    void timer_Tick(object sender, EventArgs e)
    {
        lblTime.Content = DateTime.Now.ToLongTimeString();
        GC.Collect();
    }
}

Некоторые факты, которые я знаю:

  • Несмотря на то, что сборщик мусора.NET является недетерминированным, сбор может быть вызван вызовом GC.Collect()
  • Когда выполняется сбор, уничтожается любой объект без ссылок

В обратном коде MainWindow не содержит ссылки на таймер. На мой взгляд, он должен быть уничтожен, и интерфейс больше не должен обновляться, так как я добавил некоторые вызовы GC.Collect. Но кажется, что он не работает, так как интерфейс все еще обновляется, и, следовательно, объект все еще жив.

Вопрос в том, что происходит? Я где-то ошибаюсь? Или я пропустил какой-то важный факт?

Редактировать 1: этот код на самом деле является примером, который я нашел в учебнике, в котором показано, как использовать DispatcherTimer. Но я подумал, что произойдет, когда начнется сбор мусора, поэтому я добавил этот GC.Collect, чтобы узнать, что произойдет. Благодаря хорошему количеству обучающих программ о.NET GC, которые я прочитал, я знаю, что вызов GC.Collect - плохая идея, и это не должно быть сделано, если только не исключительные случаи.

  • 1
    DispatcherTimer принимает специальные меры, чтобы остаться в живых. Это обманывает.
  • 0
    Я думаю (хотя я не уверен), что подписка на событие может остановить GC, не уверен в деталях, но я бы исследовал GC с событиями.
Показать ещё 1 комментарий
Теги:
wpf
garbage-collection

1 ответ

9
Лучший ответ

Класс Dispatcher поддерживает внутренний List<DispatcherTimer> для активных таймеров. Внутренний метод Dispatcher.AddTimer() добавляет его, вызванный методом Start(). Метод RemoveTimer() вызывается методом Stop().

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

Имейте в виду, что он продолжает тикать, если вы не вызываете Stop(), даже если окно, у которого есть обработчик события для таймера, закрывается. Такой объект окна будет течь, если вы этого не сделаете, вы получите исключение ObjectDisposedException, только если вам повезет.

  • 2
    С потенциальной утечкой крайне важно справиться, а не то, о чем я знал до этого поста. Надо следить за этим!

Ещё вопросы

Сообщество Overcoder
Наверх
Меню