线程不活动时释放资源

时间:2011-05-10 09:35:59

标签: c# multithreading backgroundworker

我正在使用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 个答案:

答案 0 :(得分:4)

你成功地关闭了组件,告诉你你做错了什么。但是你没有真正解决问题。不支持线程的组件需要STA,Single Threaded Apartment。所以它的所有方法都是从相同的线程调用的,即使调用是在另一个线程上进行的。 COM负责将调用从一个线程编组到另一个线程。 STA线程通过泵送消息循环使其成为可能。

然而,您所做的是创建另一个线程并对其进行调用,这与创建 generator 对象的线程不同。这并没有解决问题,它仍然是线程不安全的。 COM仍在召集电话。

重要的是您创建生成器对象的线程。由于它是一个单元线程对象,因此必须在STA线程上创建 。 Windows应用程序中通常只有一个,即程序的主线程,通常称为UI线程。如果你在不是STA的.NET工作线程上创建它,就像你在这里一样,那么COM将介入并创建一个STA线程本身,为组件提供一个好客的家。这很好,但通常是不受欢迎的。

这里没有免费的午餐,你不能神奇地制作明确表示它没有的代码(注册表中的ThreadingModel键)支持线程行为就像它一样。您的下一个最佳选择是创建一个STA线程并在其上运行所有代码,包括创建COM对象。请注意,您通常必须使用Application.Run()泵送消息循环,许多COM服务器假设有一个可用。特别是当他们告诉你需要一个STA线程时。你会注意到他们在行为不端时会这样做,在方法调用上遇到僵局或者没有引发事件。

关于您的原始问题,这是标准的.NET行为。垃圾收集器在需要时运行,而不是在您认为应该运行时运行。您可以使用GC.Collect()覆盖它,但这很少需要。虽然在您的情况下它可能是一个快速修复,但COM为每个文件创建一个新线程。 STA线程为 generator 提供一个home。使用Debug + Windows + Threads查看它们。在销毁COM对象之前,这些线程不会停止。这需要终结器线程运行。当有超过两千个文件时,你的代码也将消耗所有可用的内存并使用OOM炸弹,或许有足够的理由寻找真正的修复。

答案 1 :(得分:1)

这可能导致垃圾收集不是立竿见影的。在线程超出范围后尝试收集:
编辑:
您还需要实现一种更好的方法来等待线程完成其他忙碌等待(while (thread.IsAlive);)以节省CPU时间,您可以使用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);
            }
        }

答案 2 :(得分:0)

生成方法从UI控件获取数据吗?