Освобождение ресурсов, когда поток не жив - PullRequest
1 голос
/ 10 мая 2011

Я использую BackgroundWorker и внутри него я использую цикл foreach, внутри которого я создаю новый поток, жду его завершения, а затем сообщаю о прогрессе и продолжаю цикл foreach. Вот о чем я говорю:

private void DoWork(object sender, DoWorkEventArgs e) {
            var fileCounter = Convert.ToDecimal(fileNames.Count());
            decimal i = 0;
            foreach (var file in fileNames) {
                i++;
                var generator = new Generator(assembly);

                var thread = new Thread(new ThreadStart(
                        delegate() {
                            generator.Generate(file);
                        }));
                thread.SetApartmentState(ApartmentState.STA);
                thread.Start();
                while (thread.IsAlive); // critical point
                int progress = Convert.ToInt32(Math.Round(i / fileCounter * 100));
                backgroundWorker.ReportProgress(progress);
            }
        }

Проблема в том, что память не освобождается после завершения потока (после прохождения строки "критическая точка"). Я думал, что когда поток не жив, все ресурсы, связанные с ним, будут освобождены. Но, видимо, это не так. Может кто-нибудь объяснить мне, почему и что я делаю не так. Спасибо.

Ответы [ 3 ]

4 голосов
/ 10 мая 2011

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

Однако вы создали другой поток и выполняете в нем вызовы, отличные от потока, в котором генератор объект создан.Это не решает проблему, это все еще потокобезопасно.COM по-прежнему выполняет маршал вызова.

Большое значение имеет поток, в котором вы создали объект generator .Поскольку это многопоточный объект, его необходимо создать в потоке STA.Обычно в приложении Windows есть только один, основной поток вашей программы, иначе известный как поток пользовательского интерфейса.Если вы создадите его в рабочем потоке .NET, который не является STA, как вы делаете здесь, тогда COM вступит в действие и создаст сам поток STA, чтобы предоставить компоненту гостеприимный дом.Это хорошо, но обычно нежелательно.

Здесь нет бесплатного обеда, вы не можете волшебным образом создать кусок кода, который явно говорит, что не поддерживает (ключ ThreadingModel в реестре) поддержку потоковвести себя так, как он.Ваша следующая лучшая ставка - создать поток STA и запустить все кода на нем, включая создание COM-объекта.Остерегайтесь того, что вам обычно приходится закачивать цикл обработки сообщений с помощью Application.Run (), многие COM-серверы предполагают, что он доступен.Особенно, когда они говорят вам, что требуется поток STA.Вы заметите, что они делают, когда они ведут себя неправильно, блокируют вызов метода или не вызывают события.

Что касается вашего исходного вопроса, это стандартное поведение .NET.Сборщик мусора запускается тогда, когда это необходимо, а не тогда, когда вы думаете, что должны.Вы можете переопределить его с помощью GC.Collect (), но это очень редко необходимо.Хотя это может быть быстрым решением в вашем случае, COM создает новый поток для каждого отдельного файла.Нить STA, чтобы дать генератору дом.Используйте Debug + Windows + Threads, чтобы увидеть их.Эти потоки не остановятся, пока COM-объект не будет уничтожен.Что требует запуска потока финализатора.Ваш код также будет использовать всю доступную память и бомбу с OOM, когда существует более двух тысяч файлов, что, возможно, является достаточной причиной для поиска реального исправления.

1 голос
/ 10 мая 2011

Это может быть причиной того, что сборка мусора не является немедленной.Попробуйте собрать после того, как поток выйдет из области видимости:
Редактировать:
Вам также нужно реализовать лучший способ ожидания завершения потока, чем ожидание занятого (while (thread.IsAlive);), чтобы сэкономить время процессора,используйте AutoResetEvent.

private void DoWork(object sender, DoWorkEventArgs e) {
            var fileCounter = Convert.ToDecimal(fileNames.Count());
            decimal i = 0;
            var Event = new AutoResetEvent(false);
            foreach (var file in fileNames) {
                i++;
                var generator = new Generator(assembly);

                {
                    var thread = new Thread(new ThreadStart(
                            delegate() {
                                generator.Generate(file);
                                Event.Set();
                            }));
                    thread.SetApartmentState(ApartmentState.STA);
                    thread.Start();
                    //while (thread.IsAlive); // critical point
                    Event.WaitOne();
                }
                GC.Collect();
                int progress = Convert.ToInt32(Math.Round(i / fileCounter * 100));
                backgroundWorker.ReportProgress(progress);
            }
        }
0 голосов
/ 10 мая 2011

Метод Generate получает данные из элементов управления пользовательского интерфейса?

...