Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UI Thread .Invoke() causing handle leak?

In what circumstances would updating a UI control from a non-UI thread could cause the processes' handles to continually increase, when using a delegate and .InvokeRequired?

For example:

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

When this is called in a loop or on timer intervals, the handles for the program consistently increase.

EDIT:

If the above is commented out and amended as such:

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

...then the handles stop incrementing, however I don't want to allow cross thread calls, of course.

EDIT 2:

Here is a sample that shows the handles increase:

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;
}
like image 807
JYelton Avatar asked Jun 15 '10 19:06

JYelton


4 Answers

I had the same problem with

this.Invoke(new DelegateClockUpdate(ChangeClock), sender, e);

creating one handle each call.

The handle increments because Invoke is Synchronous and effectively the handle has been left hanging.

Either a Wait Handle should be used to process the result or the Asynchronous BeginInvoke method as shown below.

this.BeginInvoke(new DelegateClockUpdate(ChangeClock), sender, e);    
like image 104
peterincumbria Avatar answered Nov 19 '22 23:11

peterincumbria


The Control.Invoke() method doesn't consume any handles. However, this code is clearly called from a thread. A Thread does consume handles, 5 of them.

The Thread class doesn't have a Dispose() method, although it ought to have one. That was probably by design, it would be very difficult to call reliably, impossibly so for threadpool threads. The 5 handles that a thread requires are released by the finalizer. Your program will require ever increasing amounts of handles if the finalizer never runs.

Not getting the finalizer to run is quite unusual. You would have to have a program that starts a lot of threads but doesn't allocate a lot of memory. This tends to only happen in static tests. You can diagnose this condition with Perfmon.exe, use the .NET memory performance counters and check if gen #0 collections are being done.

If this happens in a production program then you'll have to call GC.Collect() yourself to avoid a runaway handle leak.

like image 37
Hans Passant Avatar answered Nov 19 '22 23:11

Hans Passant


I've seen the same thing in my code. I fixed it by replacing Invoke with BeginInvoke. The handle leak went away.

Doron.

like image 3
Doron Neumann Avatar answered Nov 19 '22 23:11

Doron Neumann


I actually see the same problem occuring as JYelton. I have the same call from within a thread to update the UI.

As soon as the line someControl.Invoke(new DelegateUIUpdate(UIUpdate)); is called, the handle increases by one. There is certainly a leak of some kind on the invoke, but I have no idea what is causing it. This has been verified on several systems.

like image 1
Spenduku Avatar answered Nov 20 '22 01:11

Spenduku