线程不活动时释放资源

发布于 2024-11-05 19:27:11 字数 979 浏览 0 评论 0原文

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

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

I am using BackgroundWorker and inside it I am using foreach loop, inside which i create new thread, wait for it to finish, and than report progress and continue foreach loop. Here is what I am talking about:

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);
            }
        }

The problem is that memory is not being freed after thread finishes (after "critical point" line is passed). I thought that when thread is not alive, all resources associated with it will be released. But apparently this is not true.
Can anyone explain to me why and what am I doing wrong.
Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

勿忘初心 2024-11-12 19:27:11

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

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

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

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

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

You managed to shut up the component telling you that you were doing something wrong. You however didn't actually fix the problem. An STA, Single Threaded Apartment, is required by components that do not support threading. So that all of its methods are called from the same thread, even if the call was made on another thread. COM takes care of marshaling the call from one thread to another. An STA thread makes that possible by pumping a message loop.

What you did however is create another thread and make calls on it, distinct from the thread on which the generator object was created. This doesn't solve the problem, it is still thread-unsafe. COM still marshals the call.

What matters a great deal is the thread on which you created the generator object. Since it is an apartment threaded object, it must be created on an STA thread. There normally is only one in a Windows app, the main thread of your program, otherwise commonly known as the UI thread. If you create it on a .NET worker thread that isn't STA, like you do here, then COM will step in and create an STA thread itself to give the component a hospitable home. This is nice but usually undesirable.

There's no free lunch here, you cannot magically make a chunk of code that explicitly says it doesn't (the ThreadingModel key in the registry) support threading behave like it does. Your next best bet is to create an STA thread and run all of the code on it, including the COM object creation. Beware that you typically have to pump a message loop with Application.Run(), many COM servers assume there's one available. Especially when they tell you that an STA thread is required. You'll notice that they do when they misbehave, deadlocking on a method call or not raising events.

Regarding your original question, this is standard .NET behavior. The garbage collector runs when it needs to, not when you think it should. You can override it with GC.Collect() but that's very rarely necessary. Although it might be a quick fix in your case, COM creates a new thread for every single file. The STA thread to give the generator a home. Use Debug + Windows + Threads to see them. These threads won't stop until the COM object is destroyed. Which requires the finalizer thread to run. Your code will also consume all available memory and bomb with OOM when there are more than two thousand files, perhaps reason enough to look for a real fix.

心的位置 2024-11-12 19:27:11

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

This might be cause garbage collection is not immediate. Try to collect after the thread goes out of scope:
Edit:
You also need to implement a better way to wait for the thread to finish other then busy wait(while (thread.IsAlive);) to save CPU time, you can use 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);
            }
        }
不顾 2024-11-12 19:27:11

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

Do Generate method get data from UI controls?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文