Растет количество дескрипторов при обработке событий
От: Flammable Россия  
Дата: 12.01.11 09:20
Оценка:
Столкнулся с интересной проблемой: программа с нижеприведенным кодом каждую секунду создает новый дескриптор и не закрывает его. Количество дескрипторов мониторится через Диспетчер задач.
В принципе, это пока ни к чему страшному не приводило, но мне кажется, это не есть хорошо.

using System;
using System.Windows.Forms;
using System.Threading;

namespace WindowsFormsEventsTest
{
    public partial class Form1 : Form
    {
        Thread UpdaterThread;
        event UpdateHandler eUpdate;
        EventArgs e = null;
        delegate void UpdateHandler(EventArgs e);
        delegate void UpdateCallback();
        UpdateCallback UpdaterCallback;

        public Form1()
        {
            InitializeComponent();
            UpdaterCallback = new UpdateCallback(OnUpdate);
            eUpdate += new UpdateHandler(eUpdateHandler);

            UpdaterThread = new Thread(Updater);
            UpdaterThread.IsBackground = true;
            UpdaterThread.Start();
        }

        void Updater()
        {
            while (true)
            {
                eUpdate(e);
                Thread.Sleep(1000);
            }
        }

        void eUpdateHandler(EventArgs e)
        {
            if (InvokeRequired == true)
            {
                try { Invoke(UpdaterCallback); }
                catch { Console.WriteLine("Fail"); }
            }
            else
            {
                Console.WriteLine("Update tick");
            }
        }
        void OnUpdate()
        {
            Console.WriteLine("Update tick");
        }
    }
}
Re: Растет количество дескрипторов при обработке событий
От: Lloyd Россия  
Дата: 12.01.11 09:24
Оценка:
Здравствуйте, Flammable, Вы писали:

F>Столкнулся с интересной проблемой: программа с нижеприведенным кодом каждую секунду создает новый дескриптор и не закрывает его. Количество дескрипторов мониторится через Диспетчер задач.

F>В принципе, это пока ни к чему страшному не приводило, но мне кажется, это не есть хорошо.

Что показывает тот же самый код в не-winforms приложении?
Re[2]: Растет количество дескрипторов при обработке событий
От: Flammable Россия  
Дата: 12.01.11 09:45
Оценка:
Здравствуйте, Lloyd, Вы писали:

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


F>>Столкнулся с интересной проблемой: программа с нижеприведенным кодом каждую секунду создает новый дескриптор и не закрывает его. Количество дескрипторов мониторится через Диспетчер задач.

F>>В принципе, это пока ни к чему страшному не приводило, но мне кажется, это не есть хорошо.

L>Что показывает тот же самый код в не-winforms приложении?


Тот же самый код не работает в консольном проекте (свойство InvokeRequired и метод Invoke принадлежат классу Control)
Кроме того, в нем не требуется выполнение метода одного потока в другом потоке.
Если создать консольный проект с генерацией и простой обработкой событий, то количество дескрипторов не растет.
Re: Растет количество дескрипторов при обработке событий
От: HowardLovekraft  
Дата: 12.01.11 11:39
Оценка: 16 (1)
Здравствуйте, Flammable, Вы писали:

F>Столкнулся с интересной проблемой: программа с нижеприведенным кодом каждую секунду создает новый дескриптор и не закрывает его

Это by design.

Invoke/BeginInvoke в конечном счете работают через MarshaledInvoke, который "заточен" на асинхронное выполнение.
В случае вызова через Invoke, MarshaledInvoke не возвращает управление, пока не дождется выполнения вашего делегата.
Ждет он его с помощью IAsyncResult.AsyncWaitHandle — это тот самый хэндл (событие), который "утекает".

Эксперимента ради, замените
Invoke(UpdaterCallback);

на
var ar = BeginInvoke(UpdaterCallback);
using (ar.AsyncWaitHandle)
{
  EndInvoke(ar);
}

и посмотрите на результат.

Затем замените
Thread.Sleep(1000);

на
Thread.Sleep(0);

и верните обратно вызов делегата через Invoke.

Посмотрите на результат (количество хэндлов), и не переживайте — их при необходимости уберет GC.
Re[2]: Растет количество дескрипторов при обработке событий
От: Warturtle  
Дата: 12.01.11 11:43
Оценка:
Здравствуйте, HowardLovekraft, Вы писали:

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


F>>Столкнулся с интересной проблемой: программа с нижеприведенным кодом каждую секунду создает новый дескриптор и не закрывает его

HL>Это by design.

HL>Invoke/BeginInvoke в конечном счете работают через MarshaledInvoke, который "заточен" на асинхронное выполнение.

HL>В случае вызова через Invoke, MarshaledInvoke не возвращает управление, пока не дождется выполнения вашего делегата.
HL>Ждет он его с помощью IAsyncResult.AsyncWaitHandle — это тот самый хэндл (событие), который "утекает".

HL>Эксперимента ради, замените

HL>
HL>Invoke(UpdaterCallback);
HL>

HL>на
HL>
HL>var ar = BeginInvoke(UpdaterCallback);
HL>using (ar.AsyncWaitHandle)
HL>{
HL>  EndInvoke(ar);
HL>}
HL>

HL>и посмотрите на результат.

HL>Затем замените

HL>
HL>Thread.Sleep(1000);
HL>

HL>на
HL>
HL>Thread.Sleep(0);
HL>

HL>и верните обратно вызов делегата через Invoke.

HL>Посмотрите на результат (количество хэндлов), и не переживайте — их при необходимости уберет GC.

Во-во, оно и убирает. Запустил интереса ради — где-то в районе 370 хендлов их количество сбросилось до сотни с чем-то. Ну и если вставить
void OnUpdate()
{
    Console.WriteLine("Update tick");
    GC.Collect();
}

то и вовсе не растет.
Re[2]: Растет количество дескрипторов при обработке событий
От: MxMsk Португалия  
Дата: 12.01.11 12:43
Оценка:
Здравствуйте, HowardLovekraft, Вы писали:

HL>Invoke/BeginInvoke в конечном счете работают через MarshaledInvoke, который "заточен" на асинхронное выполнение.

HL>В случае вызова через Invoke, MarshaledInvoke не возвращает управление, пока не дождется выполнения вашего делегата.
HL>Ждет он его с помощью IAsyncResult.AsyncWaitHandle — это тот самый хэндл (событие), который "утекает".
Что-то мне непонятно, а почему этот хэндл "утекает"? Код топикстартера, конечно, образец, как можно запутать человека в трех соснах, но вроде ничего криминального.
Re[3]: Растет количество дескрипторов при обработке событий
От: Flammable Россия  
Дата: 12.01.11 12:58
Оценка:
Здравствуйте, Warturtle, Вы писали:
W>Во-во, оно и убирает. Запустил интереса ради — где-то в районе 370 хендлов их количество сбросилось до сотни с чем-то. Ну и если вставить
W>
W>void OnUpdate()
W>{
W>    Console.WriteLine("Update tick");
W>    GC.Collect();
W>}
W>

W>то и вовсе не растет.

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

Здравствуйте, MxMsk, Вы писали:
MM>Что-то мне непонятно, а почему этот хэндл "утекает"? Код топикстартера, конечно, образец, как можно запутать человека в трех соснах, но вроде ничего криминального.

+1 Код действительно жуткий, писал очень давно и до сих пор не переделал.
Re[3]: Растет количество дескрипторов при обработке событий
От: HowardLovekraft  
Дата: 12.01.11 13:07
Оценка: 9 (2)
Здравствуйте, MxMsk, Вы писали:

MM>Что-то мне непонятно, а почему этот хэндл "утекает"? Код топикстартера, конечно, образец, как можно запутать человека в трех соснах, но MM>вроде ничего криминального.

ТС и его код не при чем.

Суть в том, что при запуске через Invoke идет обращение к свойству AsyncWaitHandle конкретной реализации IAsyncResult, там создается событие, и, следовательно, хэндл. Явно этот хэндл не диспозится (собственно, ответьте на вопрос, как часто вы закрываете AsyncWaitHandle при асинхронных операциях?). Когда GC решает, что пора убрать мусор, он закрывает хэндл путем вызова Finalize.

Дергать GC вручную, конечно, не нужно.
Re[4]: Растет количество дескрипторов при обработке событий
От: MxMsk Португалия  
Дата: 12.01.11 13:37
Оценка:
Здравствуйте, HowardLovekraft, Вы писали:

HL>Суть в том, что при запуске через Invoke идет обращение к свойству AsyncWaitHandle конкретной реализации IAsyncResult, там создается событие, и, следовательно, хэндл. Явно этот хэндл не диспозится (собственно, ответьте на вопрос, как часто вы закрываете AsyncWaitHandle при асинхронных операциях?). Когда GC решает, что пора убрать мусор, он закрывает хэндл путем вызова Finalize.

Ну, т.е. утечки по сути нет. Просто память пока не понадобилась. Автор MarshaledInvoke, конечно, мог бы и позаботиться о ThreadMethodEntry. Или автор ThreadMethodEntry о реализации IDisposable.
Re[5]: Растет количество дескрипторов при обработке событий
От: Flammable Россия  
Дата: 12.01.11 13:53
Оценка:
Спасибо всем за ответы!
Re[6]: Растет количество дескрипторов при обработке событий
От: Flammable Россия  
Дата: 12.01.11 21:29
Оценка: :)
Рано обрадовался.
Так-то оно работает, а в составе другого, большого проекта — нет.
В другом проекте запускается процесс, а с помощью событий на форму выводится статистика использования этим процессом процессорного времени и оперативной памяти.
Кроме статистики, события используются для опционального скрытия свернутого окна.
Пока процесс не запущен, все идет нормально — события генерируются и обрабатываются без исключений, количество дескрипторов не растет.
Но стоит запустить процесс, и моя программа выбрасывает исключение:

System.ObjectDisposedException was unhandled
Message="Доступ к ликвидированному объекту невозможен. Имя объекта: "Main"."
Source="System.Windows.Forms"
ObjectName="Main"
StackTrace:
в System.Windows.Forms.Control.EndInvoke(IAsyncResult asyncResult)
в xrServerManager.Main.SetShowInTaskBar() в E:\prog_repository\xrServerManager\xrServerManager\Main\Main.HideMinimized.cs:строка 61
в xrServerManager.Main.MainFormNormalized() в E:\prog_repository\xrServerManager\xrServerManager\Main\Main.HideMinimized.cs:строка 42
в xrServerManager.Main.WindowStateDetecter() в E:\prog_repository\xrServerManager\xrServerManager\Main\Main.HideMinimized.cs:строка 32
в System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
в System.Threading.ThreadHelper.ThreadStart()

Здесь возникает первый вопрос: почему объект ликвидирован? Main — это класс, описывающий главную форму.

Код метода, в котором возникает исключение:

        // Устанавливает свойство ShowInTaskbar = true
        private void SetShowInTaskBar()
        {
            if (this.WindowState == FormWindowState.Minimized) return;
            if (this.InvokeRequired)
            {   // ISetWindowStateCallback объявлен в классе Main следующим образом: public static IAsyncResult ISetWindowStateCallback;
                ISetWindowStateCallback = BeginInvoke(pSetWindowState_Show); // pSetWindowState_Show - ссылка на метод SetShowInTaskBar()
                using (ISetWindowStateCallback.AsyncWaitHandle)
                {
                    EndInvoke(ISetWindowStateCallback);
                }
            }
            else
            {
                this.ShowInTaskbar = true;
            }
        }



Я попробовал добавить условие (!this.Disposing && !this.IsDisposed), но возникает то же самое исключение.

        // Устанавливает свойство ShowInTaskbar = true
        private void SetShowInTaskBar()
        {
            if (this.WindowState == FormWindowState.Minimized) return;
            if (this.InvokeRequired)
            {   // ISetWindowStateCallback объявлен в классе Main следующим образом: public static IAsyncResult ISetWindowStateCallback;
                ISetWindowStateCallback = BeginInvoke(pSetWindowState_Show); // pSetWindowState_Show - ссылка на метод SetShowInTaskBar()
                using (ISetWindowStateCallback.AsyncWaitHandle)
                {
                    if (this.Disposing == false && this.IsDisposed == false)
                    {
                        EndInvoke(ISetWindowStateCallback);
                    }
                }
            }
            else
            {
                this.ShowInTaskbar = true;
            }
        }


Помогите, пожалуйста, разобраться.
Re[7]: Растет количество дескрипторов при обработке событий
От: HowardLovekraft  
Дата: 13.01.11 06:39
Оценка: +1 :)
Здравствуйте, Flammable, Вы писали:

F>Рано обрадовался.

OMG.

Код с вызовом AsyncWaitHandle.Dispose был приведен исключительно для примера. Оставьте эти хэндлы в покое.
Их уберет GC, когда в этом возникнет необходимость.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.