线程不活动时释放资源

Val*_*ale 1 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);
            }
        }
Run Code Online (Sandbox Code Playgroud)

问题是线程完成后(通过“临界点”线后)内存没有被释放。我认为当线程不活动时,与它相关的所有资源都会被释放。但显然这不是真的。任何人都可以向我解释为什么以及我做错了什么。谢谢。

Han*_*ant 5

您设法关闭了组件,告诉您您做错了什么。但是,您实际上并没有解决问题。不支持线程的组件需要一个 STA,即单线程单元。这样它的所有方法都从同一个线程调用,即使调用是在另一个线程上进行的。COM 负责将调用从一个线程编组到另一个线程。STA 线程通过泵送消息循环使这成为可能。

但是,您所做的是创建另一个线程并对其进行调用,这与创建生成器对象的线程不同。这并不能解决问题,它仍然是线程不安全的。COM 仍对呼叫进行编组。

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

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

关于您的原始问题,这是标准的 .NET 行为。垃圾收集器在需要时运行,而不是在您认为应该时运行。您可以使用 GC.Collect() 覆盖它,但这很少需要。尽管在您的情况下这可能是一个快速修复,但 COM 为每个文件创建一个新线程。STA 线程给生成器一个家。使用 Debug + Windows + Threads 来查看它们。这些线程不会停止,直到 COM 对象被销毁。这需要终结器线程运行。当有超过两千个文件时,您的代码还将消耗所有可用内存并因 OOM 爆炸,这可能足以寻找真正的修复程序。