UI Thread .Invoke()导致句柄泄漏?

JYe*_*ton 8 c# multithreading memory-leaks handles

在什么情况下,在使用委托时,从非UI线程更新UI控件可能会导致进程的句柄不断增加.InvokeRequired

例如:

public delegate void DelegateUIUpdate();
private void UIUpdate()
{
    if (someControl.InvokeRequired)
    {
        someControl.Invoke(new DelegateUIUpdate(UIUpdate));
        return;
    }
    // do something with someControl
}
Run Code Online (Sandbox Code Playgroud)

在循环或定时器间隔中调用此方法时,程序的句柄会持续增加.

编辑:

如果以上内容被注释掉并进行了修改:

public delegate void DelegateUIUpdate();
private void UIUpdate()
{
    //if (someControl.InvokeRequired)
    //{
    //   someControl.Invoke(new DelegateUIUpdate(UIUpdate));
    //    return;
    //}
    CheckForIllegalCrossThreadCalls = false;
    // do something with someControl
}
Run Code Online (Sandbox Code Playgroud)

...然后句柄停止递增,当然我不想允许跨线程调用.

编辑2:

这是一个显示句柄增加的示例:

Thread thread;
private delegate void UpdateGUI();
bool UpdateTheGui = false;

public Form1()
{
    InitializeComponent();

    thread = new Thread(new ThreadStart(MyThreadLoop));
    thread.Start();
}

private void MyThreadLoop()
{
    while (true)
    {
        Thread.Sleep(500);
        if (UpdateTheGui)
        {
            UpdateTheGui = false;
            UpdateTheGuiNow();
        }
    }
}

private void UpdateTheGuiNow()
{
    if (label1.InvokeRequired)
    {
        label1.Invoke(new UpdateGUI(UpdateTheGuiNow));
        return;
    }

    label1.Text = DateTime.Now.ToString("MM-dd-yyyy HH:mm:ss");
    label2.Text = DateTime.Now.ToString("MM-dd-yyyy HH:mm:ss");
    label3.Text = DateTime.Now.ToString("MM-dd-yyyy HH:mm:ss");
}

private void btnInvoke_Click(object sender, EventArgs e)
{
    UpdateTheGui = true;
}
Run Code Online (Sandbox Code Playgroud)

Han*_*ant 3

Control.Invoke() 方法不消耗任何句柄。然而,这段代码显然是从线程中调用的。一个线程确实会消耗句柄,其中有 5 个。

Thread 类没有 Dispose() 方法,尽管它应该有一个方法。这可能是设计使然,对于线程池线程来说,可靠地调用是非常困难的,这是不可能的。线程所需的 5 个句柄由终结器释放。如果终结器从不运行,您的程序将需要不断增加的句柄数量。

不让终结器运行是很不寻常的。您必须有一个启动大量线程但不分配大量内存的程序。这往往只发生在静态测试中。您可以使用 Perfmon.exe 诊断此情况,使用 .NET 内存性能计数器并检查第 0 代收集是否正在完成。

如果这种情况发生在生产程序中,那么您必须自己调用 GC.Collect() 以避免失控句柄泄漏。

  • 这里还有一些关于此问题的其他有用的讨论:http://stackoverflow.com/questions/1603123/how-to-avoid-leaking-handles-when-invoking-in-ui-from-system-threading-timer (2认同)