Re[9]: Порядок закрытия FileStream в Dispose
От: Fortnum  
Дата: 15.09.14 17:29
Оценка: 3 (1) +1
Здравствуйте, Sharov, Вы писали:

S>>>>"if (IsFileReady(file)) OpenFile(file);", ага.

S>>>>Чем автору всей этой схемы обычный мютекс/семафор не угодил?
S>>>А что не так в приведенном коде?
F>>Например, если в OpenFile пойдет открытие так же, как и в IsFileReady с FileShare.None, то между возвратом из IsFileReady и открытием этого файла в OpenFile, файл может быть открыт другим процессом, и открытие файла в OpenFile вызовет IOException. Смысл в IsFileReady теряется, а если исключение в/из OpenFile не будет поймано, то приведет к сбоям программы. Самое страшное, что в каком-то % случаев оно будет работать успешно, в некоторых случаях до 99.999%
S>Ну так это известный факт, и тут ничего не поделаешь. Самое главное ловить IOException.

Лучше, чтоб IsFileReady возвращал FileStream, если FileReady.
Re[7]: Порядок закрытия FileStream в Dispose
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 16.09.14 03:40
Оценка: 3 (1) +1
Здравствуйте, Fortnum, Вы писали:

F>А вот не выгружается что-то Берут сомнения, а приходят ли финализаторы?


http://msdn.microsoft.com/EN-US/library/9tcc8a46(v=VS.110,d=hv.2).aspx
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[8]: Порядок закрытия FileStream в Dispose
От: Fortnum  
Дата: 16.09.14 03:41
Оценка: -1 :)
Здравствуйте, Fortnum, Вы писали:

F>>Здравствуйте, TK, Вы писали:


F>>>>Я в замешательстве. Как до него может дойти, если он статический, а домен приложения один, выгрузки не предполагается?

TK>>>Приложение рано или поздно завершится. А перед этим придут и финализаторы.

F>>А вот не выгружается что-то Берут сомнения, а приходят ли финализаторы?

F>Да, все-таки они существуют!

А вот так уже нет А почему AppDomain.CurrentDomain.DomainUnload не вызывается? Вроде написано "is about to be unloaded", то есть уже вот-вот, но еще нет.

class AreThereFinalizersForStatic
{
    ~AreThereFinalizersForStatic()
    {
        Console.WriteLine("YES!");
        Console.ReadKey();
    }
}

class Program
{
    static AreThereFinalizersForStatic _areThereFinalizersForStatic = new AreThereFinalizersForStatic();

    static void Main()
    {
        Console.WriteLine("Press ANY key to unload current application domain and exit the application.");

        AppDomain.CurrentDomain.DomainUnload += (obj, e) =>
        {
            Console.WriteLine("Press ANY key to really unload the application domain.");
            Console.ReadKey();
        };

        Console.ReadKey();

        System.Diagnostics.Process.GetCurrentProcess().Kill(); // Kick the static finalizers away
    }
}
Re[8]: Порядок закрытия FileStream в Dispose
От: Fortnum  
Дата: 16.09.14 05:25
Оценка: -1 :)
Здравствуйте, Fortnum, Вы писали:

F>>Здравствуйте, TK, Вы писали:

F>>>>Я в замешательстве. Как до него может дойти, если он статический, а домен приложения один, выгрузки не предполагается?
TK>>>Приложение рано или поздно завершится. А перед этим придут и финализаторы.
F>>А вот не выгружается что-то Берут сомнения, а приходят ли финализаторы?
F>Да, все-таки они существуют!

А вот так еще можно от всех файналайзеров избавиться

The total execution time of all ProcessExit event handlers is limited, just as the total execution time of all finalizers is limited at process shutdown. The default is two seconds.


class AreThereFinalizersForStatic
{
    ~AreThereFinalizersForStatic()
    {
        Console.WriteLine("YES!");
    }
}

class Program
{
    static AreThereFinalizersForStatic _areThereFinalizersForStatic = new AreThereFinalizersForStatic();

    static void Main()
    {
        Console.WriteLine("Press ANY key to unload current application domain and exit the application.");

        AppDomain.CurrentDomain.ProcessExit += (obj, e) =>
        {
            Console.WriteLine("Just wait 2 sec.");
                
            Thread.Sleep(Timeout.Infinite);
        };
            
        Console.ReadKey();
    }
}
Re[7]: Порядок закрытия FileStream в Dispose
От: Sinix  
Дата: 15.09.14 12:41
Оценка: 9 (1)
Здравствуйте, Fortnum, Вы писали:

F>А как антивирус блокирует? Может ли он блокировать уже заблокированный моим процессом файл?

Нет с вероятностью 0.99. Точнее, фильтр фс антивируса срабатывает или в процессе, или до открытия файла (собственно, для прикладного кода оно выглядит одинаково). Вариант с "антивирус разбушевался и позакрывал все открытые хендлы" ничем принципиально не отличается от "отвалилась сеть/раздел", только намного менее вероятен. Отдельно от него защищаться нафиг не надо.

F>Если он просто блокирует как обычный сторонний процесс (без соблюдения порядка), то проблем нет.

Тогда ок. Мне попадались варианты реализации, которые падали уже на такой мелочи

F>Есть еще в тему вопросы по снятию бэкапа в режиме реального времени

Оххх... http://alphavss.codeplex.com/ разве что.

S>>из-за глюка ReadDirectoryChangesW типа такого (если используется для ожидания)


F>Эта штука без гарантии может терять уведомления, поэтому не используется.

Если бы только это... Там всё — от ложных срабатываний и до зависаний при слишком активной работой с директорией. Самый непредсказуемый компонент в System.IO по-моему.


S>>Чем автору всей этой схемы обычный мютекс/семафор не угодил?

F>Да почему не угодил? Угодил. А между машинами как?
Только какая-нибудь из разновидностей message queue (т.е. много писателей — один выгребающий из очереди), всё остальное, увы, работает очень своеобразно.
Про похожий баг с SMB рассказывали пару месяцев назад, с шарами на NFS, если ничего не забыл, тоже были какие-то проблемы.

На win server с 2003 по 2012 похожих граблей не наблюдал, но это не значит, что их гарантированно не будет. Короче говоря, всё тлен
Re[5]: Порядок закрытия FileStream в Dispose
От: TK Лес кывт.рф
Дата: 15.09.14 20:15
Оценка: 9 (1)
Здравствуйте, Fortnum, Вы писали:

F>Я в замешательстве. Как до него может дойти, если он статический, а домен приложения один, выгрузки не предполагается?


Приложение рано или поздно завершится. А перед этим придут и финализаторы.
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Re[3]: Порядок закрытия FileStream в Dispose
От: Sinix  
Дата: 15.09.14 10:12
Оценка: +1
Здравствуйте, Fortnum, Вы писали:

F>Задача написать библиотечку, позволяющую создавать и закрывать FileStream'ы в определенном порядке Наверное, можно задачу описать так... свой FileStream, который создает не один файл в ОС, а два. При этом инициализирует эти два файла в определенном порядке.

Это не задача, это ваше решение, только описанное другими словами

Смотрите в чём проблема (утрирую):

1. Вопрос на форуме: "как распечатать кнопку 1px * 1px заданного цвета?"

2. ... три страницы пропускаем ...

3. Уточняем вопрос: "как сделать любой контрол, обрабатывающий нажатия, и позволяющий задать себе цвет, как распечатать результат"?

4. ... ещё пару страниц !@#$%^&*() !?? ...

5. Собственно вопрос: "как сделать попиксельный редактор для иконок?"


На "неправильные" вопросы, как в п.1., когда человек уже уткнулся в тупик неправильно выбранных решений, но назад повернуть ещё не хочет, правильного ответа нет. Можно только заставить человека откатить рассуждения на несколько шагов назад и помочь найти ошибку в цепочке выбора. Это очень частый случай, вот
Автор: Sinix
Дата: 18.08.11
и вот
Автор: Sinix
Дата: 18.12.12
примеры.

Почему мне кажется, что у вас именно такая ситуация?

Потому что нет (ну ок, я не могу придумать) ни одного сценария, когда "определённый порядок инициализации" решается именно порядком закрытия FileStream.
С точки зрения ФС — фиолетово, правим даты создания-изменения так, как надо.
Если речь про работу с девайсами через CreateFile -> FileStream, как вот тут — снова нужны детали. Возможно, всё решается через наследника SafeHandle.
Если речь не о FileStream, а о другом потоке — ничего сказать нельзя без знания, какие гарантии предоставляет Flush()/Close() этого потока.

Самый худший вариант: другой софт мониторит эти файлы, и освободить файл 2 нужно только после освобождения файла 1. Тут всё зависит от кривости этого софта и от последствий "ошибочного" освобождения файла два раньше файла 1.
Если ничего страшного типа потери данных не ожидается — хватит и .Dispose()


Короче, нет самой задачи — нет "правильных" ответов, сорри.
Порядок закрытия FileStream в Dispose
От: Fortnum  
Дата: 15.09.14 04:18
Оценка:
Предположим, есть некий класс A, у которого 2 поля типа FileStream, каким-то образом инициализируются. Если для внутреннего употребления надо сделать так, чтобы эти FileStream'ы в определенном порядке деинициализировались, я правильно понимаю, что шаблон Dispose будет ошибочным вариантом, т.к. в момент исполнения A.Dispose эти FileStream'ы уже могут быть закрыты GC?

Как лучше сделать для своего внутреннего употребления?

Если в момент инициализации эти FileStream'ы помещать в какой-то статический список-поле A.List<FileStream>, а в A.Dispose после деинициализации в нужном порядке, убирать их оттуда, это хорошее решение для внутреннего употребления?

public class A : IDisposable
{
    public A() { ... }

    public FileStream FileStream1;
    public FileStream FileStream2;

    ~A()
    {
        Dispose();
    }

    public void Dispose()
    {
        File.Delete(FileStream1);

        FileStream1.Dispose();

        FileStream2.Dispose();
    }
}
Re: Порядок закрытия FileStream в Dispose
От: TK Лес кывт.рф
Дата: 15.09.14 04:46
Оценка:
Здравствуйте, Fortnum, Вы писали:

F>Предположим, есть некий класс A, у которого 2 поля типа FileStream, каким-то образом инициализируются. Если для внутреннего употребления надо сделать так, чтобы эти FileStream'ы в определенном порядке деинициализировались, я правильно понимаю, что шаблон Dispose будет ошибочным вариантом, т.к. в момент исполнения A.Dispose эти FileStream'ы уже могут быть закрыты GC?


Вы путаете Dispose и Finalize. GC знает только про Finalize. Шаблон Dispose — это реализация IDisposable и использование в нем финализатора в большинстве случаев неправильно.

F>Как лучше сделать для своего внутреннего употребления?


Для внутреннего применения лучше следить за созданными A и своевременно вызывать Dispose.
Если будет спокойнее то и A.List<FileStream> тоже мало чего гарантирует
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Re[2]: Порядок закрытия FileStream в Dispose
От: Fortnum  
Дата: 15.09.14 04:58
Оценка:
Здравствуйте, TK, Вы писали:

F>>Предположим, есть некий класс A, у которого 2 поля типа FileStream, каким-то образом инициализируются. Если для внутреннего употребления надо сделать так, чтобы эти FileStream'ы в определенном порядке деинициализировались, я правильно понимаю, что шаблон Dispose будет ошибочным вариантом, т.к. в момент исполнения A.Dispose эти FileStream'ы уже могут быть закрыты GC?

TK>Вы путаете Dispose и Finalize. GC знает только про Finalize. Шаблон Dispose — это реализация IDisposable и использование в нем финализатора в большинстве случаев неправильно.

Это понятно, что GC не знает про Dispose. Но почему вызов Dispose из финализатора — это неправильно?

F>>Как лучше сделать для своего внутреннего употребления?

TK>Для внутреннего применения лучше следить за созданными A и своевременно вызывать Dispose.

Так ведь фишка финализатора в том, что можно не следить, разве не так?

TK>Если будет спокойнее то и A.List<FileStream> тоже мало чего гарантирует


А вот это почему? Статический же.
Re: Порядок закрытия FileStream в Dispose
От: Sinix  
Дата: 15.09.14 05:40
Оценка:
Здравствуйте, Fortnum, Вы писали:

F>Предположим, есть некий класс A, у которого 2 поля типа FileStream, каким-то образом инициализируются. Если для внутреннего употребления надо сделать так, чтобы эти FileStream'ы в определенном порядке деинициализировались, я правильно понимаю, что шаблон Dispose будет ошибочным вариантом, т.к. в момент исполнения A.Dispose эти FileStream'ы уже могут быть закрыты GC?


0. Dispose pattern всё-таки надо писать правильно. Вот образец, а вот подробнейшее описание что, куда и зачем.

1. Если вы можете гарантировать, что весь код, использующий A, не забудет вызвать A.Dispose() — решение вполне рабочее.
В момент вызова IDisposable.Dispose() потоки будут живы (если вы не закрыли их ранее).

2. А вот с реализацией ~A() у вас косяки.
* Во-первых, ваш Dispose() будет вызван несколько раз: один раз из IDisposable.Dispose(), второй — из финализатора. Это лечится GC.SuppressFinalize().
* Во-вторых, IDisposable.Dispose() может быть вызван повторно пользовательским кодом (например, чтобы освободить ресурс чуть пораньше). Это абсолютно нормальная практика, в Dispose лучше вставить проверку на повторный вызов (см ссылку на образец выше)
* В-третьих,
    File.Delete(FileStream1);
    FileStream1.Dispose();

скорее всего бросит IOException. Порядок надо поменять.

* В четвёртых, порядок вызова финализаторов ничем не гарантируется (про CriticalFinalizerObject пока забыли). В момент вызова ~A() любой из потоков уже может быть закрыт.

В общем, ~A вас никак не спасёт, зато .Dispose() должно хватить.


F>Если в момент инициализации эти FileStream'ы помещать в какой-то статический список-поле A.List<FileStream>, а в A.Dispose после деинициализации в нужном порядке, убирать их оттуда, это хорошее решение для внутреннего употребления?

Если не забудете про блокировки и не пугают возможные утечки ресурсов, то, в принципе, решение рабочее. Но ничем не лучше варианта с Dispose().

Как всегда, опишите задачу, которую пытаетесь решить. Все симптомы, что из "простой код, работает в 99% случаев" и "монстрокод, работает в 99.1% случаев" у вас вырисовывается 99.1
Re[2]: Порядок закрытия FileStream в Dispose
От: Fortnum  
Дата: 15.09.14 06:28
Оценка:
Здравствуйте, Sinix, Вы писали:

F>>Предположим, есть некий класс A, у которого 2 поля типа FileStream, каким-то образом инициализируются. Если для внутреннего употребления надо сделать так, чтобы эти FileStream'ы в определенном порядке деинициализировались, я правильно понимаю, что шаблон Dispose будет ошибочным вариантом, т.к. в момент исполнения A.Dispose эти FileStream'ы уже могут быть закрыты GC?


S>1. Если вы можете гарантировать, что весь код, использующий A, не забудет вызвать A.Dispose() — решение вполне рабочее.

S>В момент вызова IDisposable.Dispose() потоки будут живы (если вы не закрыли их ранее).

Да, вот это "если вы не закрыли их ранее" — один из косяков. Более того, кто-то может попробовать работать с этими потоками после вызова A.Dispose Но это для внутреннего употребления, поэтому чисто джентльменское соглашение... хотя...

S>2. А вот с реализацией ~A() у вас косяки.

S>* Во-первых, ваш Dispose() будет вызван несколько раз: один раз из IDisposable.Dispose(), второй — из финализатора. Это лечится GC.SuppressFinalize().
S>* Во-вторых, IDisposable.Dispose() может быть вызван повторно пользовательским кодом (например, чтобы освободить ресурс чуть пораньше). Это абсолютно нормальная практика, в Dispose лучше вставить проверку на повторный вызов (см ссылку на образец выше)

Спасибо. Это я всё в курсе, это всё будет учтено. Хотел конкретно вопрос порядка осветить.

S>* В-третьих,

S>
S>    File.Delete(FileStream1);
S>    FileStream1.Dispose();
S>

S>скорее всего бросит IOException. Порядок надо поменять.

Не должен бросить, т.к. FileStream1 открыт с FileShare.Delete. Вообще, тут есть вопрос... сейчас задам его отдельной веткой.

S>Как всегда, опишите задачу, которую пытаетесь решить. Все симптомы, что из "простой код, работает в 99% случаев" и "монстрокод, работает в 99.1% случаев" у вас вырисовывается 99.1


Задача написать библиотечку, позволяющую создавать и закрывать FileStream'ы в определенном порядке Наверное, можно задачу описать так... свой FileStream, который создает не один файл в ОС, а два. При этом инициализирует эти два файла в определенном порядке.
Re[4]: Порядок закрытия FileStream в Dispose
От: Fortnum  
Дата: 15.09.14 10:22
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Самый худший вариант: другой софт мониторит эти файлы, и освободить файл 2 нужно только после освобождения файла 1. Тут всё зависит от кривости этого софта и от последствий "ошибочного" освобождения файла два раньше файла 1.

S>Если ничего страшного типа потери данных не ожидается — хватит и .Dispose()

Вот-вот. Этот вариант. Потеря данных ожидается, последовательность открытия эту проблему и решает. Только почему он самый худший?
Re[5]: Порядок закрытия FileStream в Dispose
От: Sinix  
Дата: 15.09.14 11:44
Оценка:
Здравствуйте, Fortnum, Вы писали:


S>>Самый худший вариант: другой софт мониторит эти файлы...

F>Вот-вот. Этот вариант. Потеря данных ожидается, последовательность открытия эту проблему и решает. Только почему он самый худший?

Потому что очень легко словить грабли из-за блокирования файла антивирусом, глюка ReadDirectoryChangesW типа такого (если используется для ожидания) или загадить производительность слишком частыми обращениями к файлу (особенно забавно, когда файл большой и superfetch начинает его тащить в память).

Самое прелестное, если код проверки выглядит как-то так :
    public static bool IsFileReady(String sFilename)
    {
        try
        {
            using (FileStream inputStream = File.Open(sFilename, FileMode.Open, FileAccess.Read, FileShare.None))
            {
                if (inputStream.Length > 0)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }
        catch (Exception)
        {
            return false;
        }
    }


"if (IsFileReady(file)) OpenFile(file);", ага.

Чем автору всей этой схемы обычный мютекс/семафор не угодил?
Re[6]: Порядок закрытия FileStream в Dispose
От: Fortnum  
Дата: 15.09.14 12:08
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Потому что очень легко словить грабли из-за блокирования файла антивирусом


А как антивирус блокирует? Может ли он блокировать уже заблокированный моим процессом файл? Если он просто блокирует как обычный сторонний процесс (без соблюдения порядка), то проблем нет. Главное, чтобы этот сторонний процесс ничего в само содержимое файла без взятия нужных блокировок не писал. А читать — без проблем.

Есть еще в тему вопросы по снятию бэкапа в режиме реального времени

S>из-за глюка ReadDirectoryChangesW типа такого (если используется для ожидания)


Эта штука без гарантии может терять уведомления, поэтому не используется.

S>"if (IsFileReady(file)) OpenFile(file);", ага.


Не, такого нет 100%

S>Чем автору всей этой схемы обычный мютекс/семафор не угодил?


Да почему не угодил? Угодил. А между машинами как?
Re[3]: Порядок закрытия FileStream в Dispose
От: TK Лес кывт.рф
Дата: 15.09.14 16:22
Оценка:
Здравствуйте, Fortnum, Вы писали:

F>Это понятно, что GC не знает про Dispose. Но почему вызов Dispose из финализатора — это неправильно?


Посмотрите в MSDN "каноническую" реализацию Disposable паттерна — в финализаторе надо освобождать только unmanaged ресурсы. А FileStream вполне себе managed.

F>Так ведь фишка финализатора в том, что можно не следить, разве не так?


Фишка финализатора в том, что если не уследить то, оно может быть и уберет, потом, если время дойдет.

TK>>Если будет спокойнее то и A.List<FileStream> тоже мало чего гарантирует

F>А вот это почему? Статический же.

Статический это не значит, что и до него очередь не дойдет.
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Re[4]: Порядок закрытия FileStream в Dispose
От: Fortnum  
Дата: 15.09.14 16:43
Оценка:
Здравствуйте, TK, Вы писали:

F>>Это понятно, что GC не знает про Dispose. Но почему вызов Dispose из финализатора — это неправильно?

TK>Посмотрите в MSDN "каноническую" реализацию Disposable паттерна — в финализаторе надо освобождать только unmanaged ресурсы. А FileStream вполне себе managed.

Да вроде нет запрета явного на проведение действий с managed-ресурсами в финализаторе. Надо просто иметь в виду, что это другой поток, и нюансы с тем, что порядок выполнения финализаторов не определен. По мне так это как раз тот случай, когда можно и поиметь в виду С другой стороны, нюанс в том, что это будет весьма неожидано.

Кстати, по ходу вопрос, вроде раньше было так, что все остальные потоки приостанавливались на время сбора мусора, который проводился в одном своём потоке, но это поведение в будущем не гарантировалось. Я ничего не путаю? Так и есть до сих пор?

F>>Так ведь фишка финализатора в том, что можно не следить, разве не так?

TK>Фишка финализатора в том, что если не уследить то, оно может быть и уберет, потом, если время дойдет.

Вообще, конечно, нафига козе баян — что в лоб, что по лбу, но при выборе из 2-х вариантов: "поздно" и "никогда", даже не знаю что выбрать Кроме того, в using { } красиво смотрится Но количество нюансов все же подсказывает, что лучше так не делать, да?

TK>>>Если будет спокойнее то и A.List<FileStream> тоже мало чего гарантирует

F>>А вот это почему? Статический же.
TK>Статический это не значит, что и до него очередь не дойдет.

Я в замешательстве. Как до него может дойти, если он статический, а домен приложения один, выгрузки не предполагается?
Re[6]: Порядок закрытия FileStream в Dispose
От: Sharov Россия  
Дата: 15.09.14 16:47
Оценка:
Здравствуйте, Sinix, Вы писали:

S>"if (IsFileReady(file)) OpenFile(file);", ага.


S>Чем автору всей этой схемы обычный мютекс/семафор не угодил?


А что не так в приведенном коде?
Кодом людям нужно помогать!
Re[7]: Порядок закрытия FileStream в Dispose
От: Fortnum  
Дата: 15.09.14 16:56
Оценка:
Здравствуйте, Sharov, Вы писали:

S>>"if (IsFileReady(file)) OpenFile(file);", ага.

S>>Чем автору всей этой схемы обычный мютекс/семафор не угодил?
S>А что не так в приведенном коде?

Например, если в OpenFile пойдет открытие так же, как и в IsFileReady с FileShare.None, то между возвратом из IsFileReady и открытием этого файла в OpenFile, файл может быть открыт другим процессом, и открытие файла в OpenFile вызовет IOException. Смысл в IsFileReady теряется, а если исключение в/из OpenFile не будет поймано, то приведет к сбоям программы. Самое страшное, что в каком-то % случаев оно будет работать успешно, в некоторых случаях до 99.999%
Re[8]: Порядок закрытия FileStream в Dispose
От: Sharov Россия  
Дата: 15.09.14 17:19
Оценка:
Здравствуйте, Fortnum, Вы писали:

F>Здравствуйте, Sharov, Вы писали:


S>>>"if (IsFileReady(file)) OpenFile(file);", ага.

S>>>Чем автору всей этой схемы обычный мютекс/семафор не угодил?
S>>А что не так в приведенном коде?

F>Например, если в OpenFile пойдет открытие так же, как и в IsFileReady с FileShare.None, то между возвратом из IsFileReady и открытием этого файла в OpenFile, файл может быть открыт другим процессом, и открытие файла в OpenFile вызовет IOException. Смысл в IsFileReady теряется, а если исключение в/из OpenFile не будет поймано, то приведет к сбоям программы. Самое страшное, что в каком-то % случаев оно будет работать успешно, в некоторых случаях до 99.999%


Ну так это известный факт, и тут ничего не поделаешь. Самое главное ловить IOException.
Кодом людям нужно помогать!
Re[6]: Порядок закрытия FileStream в Dispose
От: Fortnum  
Дата: 16.09.14 03:29
Оценка:
Здравствуйте, TK, Вы писали:

F>>Я в замешательстве. Как до него может дойти, если он статический, а домен приложения один, выгрузки не предполагается?

TK>Приложение рано или поздно завершится. А перед этим придут и финализаторы.

А вот не выгружается что-то Берут сомнения, а приходят ли финализаторы?

static void Main()
{
    Console.WriteLine("Press ANY key to unload current application domain and exit the application.");

    AppDomain.CurrentDomain.DomainUnload += (obj, e) =>
    {
        Console.WriteLine("Press ANY key to really unload the application domain.");
        Console.ReadKey();
    };

    Console.ReadKey();
}
Re[7]: Порядок закрытия FileStream в Dispose
От: Fortnum  
Дата: 16.09.14 03:34
Оценка:
Здравствуйте, Fortnum, Вы писали:

F>Здравствуйте, TK, Вы писали:


F>>>Я в замешательстве. Как до него может дойти, если он статический, а домен приложения один, выгрузки не предполагается?

TK>>Приложение рано или поздно завершится. А перед этим придут и финализаторы.

F>А вот не выгружается что-то Берут сомнения, а приходят ли финализаторы?


Да, все-таки они существуют!

class AreThereFinalizersForStatic
{
    ~AreThereFinalizersForStatic()
    {
        Console.WriteLine("YES!");
        Console.ReadKey();
    }
}

class Program
{
    static AreThereFinalizersForStatic _areThereFinalizersForStatic = new AreThereFinalizersForStatic();

    static void Main()
    {
        Console.WriteLine("Press ANY key to unload current application domain and exit the application.");

        AppDomain.CurrentDomain.DomainUnload += (obj, e) =>
        {
            Console.WriteLine("Press ANY key to really unload the application domain.");
            Console.ReadKey();
        };

        Console.ReadKey();
    }
}
Re[8]: Порядок закрытия FileStream в Dispose
От: Fortnum  
Дата: 16.09.14 04:36
Оценка:
Здравствуйте, AndrewVK, Вы писали:

F>>А вот не выгружается что-то Берут сомнения, а приходят ли финализаторы?

AVK>http://msdn.microsoft.com/EN-US/library/9tcc8a46(v=VS.110,d=hv.2).aspx

Нашел интересную статью в тему. Наконец-то понял, почему DanegrousGetHandle такой dangerous

Keep Your Code Running with the Reliability Features of the .NET Framework

A graceful application domain unload uses graceful thread aborts and will allow any relevant finally blocks and any finalizers for objects in that domain to run, but that guarantee is not made for rude application domain unloads (unless in a CER). With rude application domain unloads, the runtime makes no guarantees about back-out code or normal finalizers running. How can a system be reliable under these conditions?

The .NET Framework 2.0 introduces a new kind of finalizer, referred to as a critical finalizer. A critical finalizer is a special kind of finalizer in that it runs even during a rude application domain unload, and it runs within a CER. To implement a critical finalizer, simply derive the class in question from the CriticalFinalizerObject class.

Any finalizer you implement in your derived class will be called as part of a CER and must have reliability guarantees that match those of the reliability contract on CriticalFinalizerObject.Finalize. In other words, you should only derive from CriticalFinalizerObject if your finalizer is guaranteed to succeed and is guaranteed not to corrupt any state when invoked under a CER. When an object that derives from CriticalFinalizerObject is instantiated, the runtime prepares the Finalize method then and there, so it won't have to JIT any code (or do other preparation) when it comes time to actually run the method for the first time.

One of the classes in the .NET Framework that derives from CriticalFinalizerObject is SafeHandle, which lives in the System.Runtime.InteropServices namespace. SafeHandle is a very welcome addition to the .NET Framework and goes a long way towards solving many of the reliability-related problems present in previous versions. At its core, SafeHandle is simply a managed wrapper around an IntPtr with a finalizer that knows how to release the underlying resource referenced by that IntPtr. Since SafeHandle derives from CriticalFinalizerObject, this finalizer is prepared when SafeHandle is instantiated, and will be called from within a CER to ensure that asynchronous thread aborts do not interrupt the finalizer.

Consider the Win32 FindFirstFile function, which is used to enumerate files in a directory. Unfortunately, if an asynchronous exception is thrown after FindFirstFile returns but before the resulting IntPtr handle is stored, that operating system resource is now leaked without any decent hopes for freeing it. SafeHandle comes to the rescue.

The only difference here is that I've replaced the IntPtr return type with SafeFindHandle, where SafeFindHandle is a custom type that derives from SafeHandle. When the runtime invokes a call to FindFirstFile, it first creates an instance of SafeFindHandle. When FindFirstFile returns, the runtime stores the resulting IntPtr into the already created SafeFindHandle. The runtime guarantees that this operation is atomic, meaning that if the P/Invoke method successfully returns, the IntPtr will be stored safely inside the SafeHandle. Once inside the SafeHandle, even if an asynchronous exception occurs and prevents FindFirstFile's SafeFindHandle return value from being stored, the relevant IntPtr is already stored within a managed object whose finalizer will ensure its proper release.

In addition to resource lifetime management, safe handles provide other benefits. First, they aid in memory management by reducing graph promotions for finalization. In the .NET Framework 1.x, a class that requires unmanaged resources typically stores the relevant IntPtr in the class, along with any other managed objects the class relies on. This class almost certainly implements a finalizer to ensure that the IntPtr is properly released. Since finalizable objects always survive at least one garbage collection, the net result is that the entire object graph starting from the class in question survives a collection, all because of that one IntPtr. Since SafeHandle now replaces that IntPtr, and since SafeHandle has its own finalizer, in most scenarios the class that stores the SafeHandle no longer needs its own finalizer. This eliminates those GC graph promotion, thus reducing the stress on the GC. Additionally, removing the finalizer typically means you can get rid of calls to GC.KeepAlive(this) in those objects.

Re[9]: Порядок закрытия FileStream в Dispose
От: Fortnum  
Дата: 16.09.14 05:10
Оценка:
Здравствуйте, Fortnum, Вы писали:

F>А почему AppDomain.CurrentDomain.DomainUnload не вызывается? Вроде написано "is about to be unloaded", то есть уже вот-вот, но еще нет.


Прочитал еще раз. Увидел: "This event is never raised in the default application domain".
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.