.NET Как побороть 2й вызов финализатора

216
31 марта 2018, 13:35

Не могу побороть, даже не побороть, а отловить когда происходит 2й вызов финализатора объекта. Все 3 объекта, должны быть уничтожены в определенном порядке, если это произойдет в произвольном порядке, то возможны либо утечки памяти, либо уничтожение статического неуправляемого объекта который отвечает за работоспособность 3х представленных объектов

Создание объектов:

  1. VlcInstance
  2. VlcMediaPlayer / VlcMedia
  3. VlcMedia / VlcMediaPlayer

Уничтожение:

  1. VlcMedia
  2. VlcMediaPlayer
  3. VlcInstance

В случае удаления функции финализаторов объектов, и использованием исключительно деструктора, все классы работают именно так как нужно.

Если возможно то подскажите где в данном коде вызываются финализаторы, за исключением выхода из Main

Код c# который управляет объектами:

using System;
namespace libvlc.net.test.csharp
{
    class Program
    {
        static VlcInstance vlcInstance;
        static VlcMediaPlayer vlcPlayer;
        static VlcMedia vlcMedia;
        static void Main(string[] args)
        {
            string path = System.IO.Path.Combine("..\\..\\libvlc-data\\2.2.6\\bin\\", IntPtr.Size == 4 ? "x86\\" : "x64");
            vlcInstance = new VlcInstance(path, "-I", "logger,none", "-vv", "--no-plugins-cache", "--ignore-config");
            vlcPlayer = new VlcMediaPlayer(vlcInstance);
            ConsoleKeyInfo cki;
            while(((cki = Console.ReadKey(true)).Key != ConsoleKey.Escape))
            {
                switch (cki.Key)
                {
                    case ConsoleKey.Spacebar:
                        vlcPlayer.TooglePause();
                        break;
                    case ConsoleKey.RightArrow:
                        vlcPlayer.Stop();
                        if (!(vlcMedia is null))
                        {
                            vlcMedia.MediaEvent -= VlcMedia_MediaEvent;
                            vlcMedia.Dispose();
                        }
                        vlcMedia = new VlcMedia(vlcInstance,
                            new Uri(System.IO.Path.Combine("https://raw.githubusercontent.com/isyami/libvlc.net/master/", "test2")));
                        vlcMedia.MediaEvent += VlcMedia_MediaEvent;
                        vlcPlayer.Open(vlcMedia);
                        break;
                    case ConsoleKey.LeftArrow:
                        vlcPlayer.Stop();
                        if (!(vlcMedia is null))
                        {
                            vlcMedia.MediaEvent -= VlcMedia_MediaEvent;
                            vlcMedia.Dispose();
                        }
                        vlcMedia = new VlcMedia(vlcInstance,
                            new Uri(System.IO.Path.Combine("https://raw.githubusercontent.com/isyami/libvlc.net/master/", "test")));
                        vlcMedia.MediaEvent += VlcMedia_MediaEvent;
                        vlcPlayer.Open(vlcMedia);
                        break;
                }
            }
            vlcMedia = null;
            vlcPlayer = null;
            vlcInstance = null;
            GC.WaitForPendingFinalizers();
            GC.Collect();
        }
        private static void VlcMedia_MediaEvent(object s, VlcMediaEventArgs a)
        {
            switch (a.EventType)
            {
                case VlcMediaEvent.MetaChanged:
                    Console.WriteLine($"{a.EventType}: \t\t{a.EventData.NewMeta}");
                    break;
                case VlcMediaEvent.SubItemAdded:
                    Console.WriteLine($"{a.EventType}: NewSubItem");
                    break;
                case VlcMediaEvent.DurationChanged:
                    Console.WriteLine($"{a.EventType}: \t{a.EventData.NewDuration}");
                    break;
                case VlcMediaEvent.ParsedChanged:
                    Console.WriteLine($"{a.EventType}: \t\t{a.EventData.NewParsed}");
                    break;
                case VlcMediaEvent.Freed:
                    Console.WriteLine($"{a.EventType}: \t\tMediaFreed");
                    break;
                case VlcMediaEvent.StateChanged:
                    Console.WriteLine($"{a.EventType}: \t\t{a.EventData.NewState}");
                    break;
                default:
                    break;
            }
        }
    }
}
Answer 1

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

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

Если вам нужно контролировать удаление объектов, вам стоит воспользоваться интерфейсом IDisposable. Вам придётся менять дизайн. Сорри.

Answer 2

Проблема решается очень просто, если после уничтожения неуправляемого объекта в деструкторе вызвать:

GC::SuppressFinalize(this);

То, повторный вызов не будет произведен.

READ ALSO
Как вытянуть функцию QuickReduct из RdotNet

Как вытянуть функцию QuickReduct из RdotNet

Кто работал с RdotNet покажите пример, как получить матрицу различимости через QuickReduct в свой массив (C#)

174
DateTimePicker взять нужный день для отчёта C# (Winforms)

DateTimePicker взять нужный день для отчёта C# (Winforms)

Добрый вечер всемПервый вопрос

156
Вывод List<> на DataGridView WinForms

Вывод List<> на DataGridView WinForms

Здравствуйте! Мне нужно сделать, чтоб пользователь вводил в TextBox'ы данные, они сохранялись в списке и добавлялись на DataGridViewДобавление в список...

202