【问题标题】:Freeing resources when thread is not alive线程不活动时释放资源
【发布时间】:2011-05-10 09:35:58
【问题描述】:

我正在使用 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);
            }
        }

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

【问题讨论】:

  • 如果要等待它完成,为什么要使用单独的线程?只需在主线程中生成即可。
  • @Dani 我不能,因为在 Generate 中引发了异常:“调用线程必须是 STA,因为许多 UI 组件都需要这个......”这就是我创建线程并将 ApartmentState 明确设置为 STA 的原因.

标签: c# multithreading backgroundworker


【解决方案1】:

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

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

重要的是您创建 generator 对象的线程。由于它是一个单元线程对象,它必须在 STA 线程上创建。 Windows 应用程序中通常只有一个,即程序的主线程,也就是俗称的 UI 线程。如果您在不是 STA 的 .NET 工作线程上创建它,就像您在此处所做的那样,那么 COM 将介入并自行创建一个 STA 线程,以便为组件提供一个好客的家。这很好,但通常不受欢迎。

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

关于您最初的问题,这是标准的 .NET 行为。垃圾收集器在需要时运行,而不是在您认为应该时运行。您可以使用 GC.Collect() 覆盖它,但这很少需要。尽管在您的情况下它可能是一个快速修复,但 COM 会为每个文件创建一个新线程。 STA 线程给 generator 一个家。使用 Debug + Windows + Threads 来查看它们。在 COM 对象被销毁之前,这些线程不会停止。这需要终结器线程运行。当有超过 2000 个文件时,您的代码也会消耗所有可用内存并使用 OOM 进行轰炸,这可能有足够的理由寻找真正的解决方案。

【讨论】:

  • 非常感谢您的回答。我明白了大部分,但我不知道该怎么做。 “您的下一个最佳选择是创建一个 STA 线程并在其上运行所有代码,包括 COM 对象创建。请注意,您通常必须使用 Application.Run() 泵送消息循环,许多 COM服务器假设有一个可用的。“?我应该创建一个线程并在其中运行后台工作者吗?
  • 如果你已经创建了一个线程,那么创建 BGW 是没有意义的。这会产生更多问题,BGW 永远不是 STA。查看此答案以获取示例:stackoverflow.com/questions/4269800/…
  • 好的,我再分析一下,看看能做些什么。
  • 虽然这可能不是最好的例子,因为它依赖于 COM 对象引发的事件来运行代码。计时器可以引发事件。不要低估沙漏光标的用处。
  • 我使用 BGW 是因为我想在每次处理一个文件时向用户显示进度条。我会看看如何删除它...可能明天,客户只是给了我一些要修复的错误:)
【解决方案2】:

这可能是因为垃圾收集没有立即进行。线程超出范围后尝试收集:
编辑:
您还需要实现一种更好的方法来等待线程完成,然后再忙等待(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);
            }
        }

【讨论】:

  • 我已经尝试过了。什么都没有发生。占用的内存还是一样的。
  • 生成器是否有机会在线程范围之外设置一些变量?
  • 也许,因为它是从外部程序集创建类型的实例。
  • 尝试处理它创建的每一件事
  • AutoResetEvent 帮助释放 CPU,正如你所说,但内存没有被释放......有没有办法显式释放线程正在使用的所有资源?
【解决方案3】:

Generate 方法是否从 UI 控件获取数据?

【讨论】:

  • 是的。这是它的主要任务。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多