Freeing resources when thread is not alive

最后都变了- 提交于 2019-12-02 10:36:22

问题


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.


回答1:


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.




回答2:


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



回答3:


Do Generate method get data from UI controls?



来源:https://stackoverflow.com/questions/5948075/freeing-resources-when-thread-is-not-alive

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!