可以在后台线程中运行GC.Collect吗? [英] Is it OK to run GC.Collect in a background thread?

查看:456
本文介绍了可以在后台线程中运行GC.Collect吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

遵循这个SO回答,我在做:

< pre $ ThreadPool.QueueUserWorkItem(
委托
{
GC.Collect();
GC.WaitForPendingFinalizers();
GC .Collect();
});

我的目标是在关闭一个大型的WinForms表单后, PictureBox控件,以确保我没有内存中的图像了。 (我相信我遵循 Jon Skeet的指示)。



我在后台线程中这样做是为了让我的UI响应。



我的问题:



它是否为我在后台线程中执行垃圾收集带来了好处?或者它真的让我的应用程序变得更慢/挂起更长?

背景当你这样做。或者换句话说,无论如何,无论您是否从工作线程执行此操作,您的UI线程都将被暂停。唯一可行的方法是GC.WaitForPendingFinalizers()花费大量时间。这实际上并不是你应该等待的东西,没有任何意义,如果它不仅仅是一眨眼的功能,那么你在代码中隐藏了非常严重的错误。



另一个重要的缺点是Windows的工作站版本给予任何拥有前景窗口的线程更大的量子。换句话说,它允许烧写比后台线程长的内核。一个简单的方法就是让Windows对用户做出更好的响应。



太多的移动部件,最好测试一下你的理论,这样你就可以确定在工作人员上运行集合实际上是您未来的事情。测量UI线程挂起非常简单,您可以使用Timer来执行此操作。它的Tick事件在线程暂停时无法运行。开始一个新的Winforms项目,在窗体上放置一个Timer,将其Interval设置为1并将Enabled设置为True,添加一个Label并使用此代码来衡量延迟:

  int prevtick = 0; 
int maxtick = -1;

private void timer1_Tick(object sender,EventArgs e){
int tick = Environment.TickCount;
if(prevtick> 0){
int thistick = tick - prevtick;
if(thistick> maxtick){
maxtick = thistick;
label1.Text = maxtick.ToString();
}
}
prevtick = tick;
}

运行你的程序,你应该在标签中看到16。如果你少了那么你应该固定你的机器,而不是其他任何影响这个测试的东西。添加一个按钮来重置测量:

pre $私人空间button1_Click(object sender,EventArgs e){
maxtick = - 1;
}

添加复选框和另一个按钮。我们会让它执行实际的集合:

  private void button2_Click(object sender,EventArgs e){
var useworker = checkBox1.Checked; (int ix = 0; ix <500 *)的
System.Threading.ThreadPool.QueueUserWorkItem((_)=> {
var lst = new List< object> 1024 * 1024 /(IntPtr.Size * 3); ++ ix){
lst.Add(new object());
}
lst.Clear();
if(useworker){
GC.Collect();
GC.WaitForPendingFinalizers();
}
else {
this.BeginInvoke(new Action((=)= > {
GC.Collect();
GC.WaitForPendingFinalizers();
)));
}
});
}

玩这个游戏,点击button2开始收集并注意值在标签中。打开复选框,使其在工作人员上运行并进行比较。使用button1重置之间的最大值。并修改分配代码,您可能想要使用位图来做任何事情,无论您如何要求这种黑客行为。



我看到:执行收集时〜220毫秒的延迟在UI线程上,在工作人员上运行时延迟〜340毫秒。显然,这完全不是改进。从我坐的地方,你的理论就死在水里。请自己尝试一下,我只有一个数据点。请注意,它将在服务器版本的Windows上显得非常不同,或者在.config文件中使用< gcServer = true> 。别的东西你可以玩。


Following this SO answer, I'm doing:

ThreadPool.QueueUserWorkItem(
    delegate
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
    });

My goal is to do a garbage collection run after I close a large WinForms form with lots of images/PictureBox controls to ensure I have no images in memory anymore. (I do believe I follow the instructions of Jon Skeet).

I'm doing it in a background thread in order to try to have my UI responsive.

My question:

Does it bring me any benefits to do the garbage collection in a background thread? Or does it actually make my application slower/hang longer?

解决方案

You are throwing away the option to have garbage collection performed on the background when you do this. Or in other words, your UI thread is going to get suspended anyway, regardless if you do this from a worker thread. The only possible way to be ahead is when GC.WaitForPendingFinalizers() is taking a substantial amount of time. It is not actually something you should ever be waiting for, there is no point, and if it takes more than the blink of an eye then you are hiding pretty serious bugs in your code.

Another significant wrinkle is that the workstation version of Windows gives any thread that owns the foreground window a larger quantum. In other words, it is allowed to burn core longer than a background thread. A simple hack to make Windows more responsive to the user.

Too many moving parts, it is really rather best to test your theory so you can be sure that running a collection on a worker is actually something you are ahead with. Measuring UI thread suspensions is pretty simple, you can use a Timer to do this. Its Tick event cannot run when the thread is suspended. Start a new Winforms project, drop a Timer on the form, set its Interval to 1 and Enabled to True, add a Label and use this code to measure delays:

    int prevtick = 0;
    int maxtick = -1;

    private void timer1_Tick(object sender, EventArgs e) {
        int tick = Environment.TickCount;
        if (prevtick > 0) {
            int thistick = tick - prevtick;
            if (thistick > maxtick) {
                maxtick = thistick;
                label1.Text = maxtick.ToString();
            }
        }
        prevtick = tick;
    }

Run your program, you should be seeing 16 in the label. If you get less then you ought to get your machine fixed, not otherwise anything that affects this test. Add a button to reset the measurement:

    private void button1_Click(object sender, EventArgs e) {
        maxtick = -1;
    }

Add a checkbox and another button. We'll have it perform the actual collection:

    private void button2_Click(object sender, EventArgs e) {
        var useworker = checkBox1.Checked;
        System.Threading.ThreadPool.QueueUserWorkItem((_) => {
            var lst = new List<object>();
            for (int ix = 0; ix < 500 * 1024 * 1024 / (IntPtr.Size * 3); ++ix) {
                lst.Add(new object());
            }
            lst.Clear();
            if (useworker) {
                GC.Collect();
                GC.WaitForPendingFinalizers();
            }
            else {
                this.BeginInvoke(new Action(() => {
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                }));
            }
        });
    }

Play with this, hit button2 to start the collection and pay attention to the value in the Label. Turn on the checkbox so it runs on the worker and compare. Use button1 to reset the maximum in between. And modify the allocation code, you probably want to do something with bitmaps, whatever you do to require this hack.

What I see: ~220 msec delay when performing the collection on the UI thread, ~340 msec delay when running on the worker. Clearly, this is not an improvement at all. From where I sit, your theory is dead in the water. Please try this yourself, I've got only one datapoint. Do beware that it is going to look very different on a server version of Windows or with <gcServer=true> in the .config file. Something else you can play with.

这篇关于可以在后台线程中运行GC.Collect吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆