It seems like calling Invoke on a winforms control in a callback from a System.Threading.Timer leaks handles until the timer is disposed. Does anyone have an idea of how to work around this? I need to poll for a value every second and update the UI accordingly.
I tried it in a test project to make sure that that was indeed the cause of the leak, which is simply the following:
System.Threading.Timer timer;
public Form1()
{
InitializeComponent();
timer = new System.Threading.Timer(new System.Threading.TimerCallback(DoStuff), null, 0, 500);
}
void DoStuff(object o)
{
this.Invoke(new Action(() => this.Text = "hello world"));
}
This will leak 2 handles/second if you watch in the windows task manager.
Invoke acts like a BeginInvoke/EndInvoke pair in that it posts the message to the UI thread, creates a handle, and waits on that handle to determine when the Invoked method is complete. It is this handle that is "leaking." You can see that these are unnamed events using Process Explorer to monitor the handles while the application is running.
If IASyncResult was IDisposable, disposal of the object would take care of cleaning up the handle. As it is not, the handles get cleaned up when the garbage collector runs and calls the finalizer of the IASyncResult object. You can see this by adding a GC.Collect() after every 20 calls to DoStuff -- the handle count drops every 20 seconds. Of course, "solving" the problem by adding calls to GC.Collect() is the wrong way to address the issue; let the garbage collector do its own job.
If you do not need the Invoke call to be synchronous, use BeginInvoke instead of Invoke and do not call EndInvoke; the end-result will do the same thing but no handles will be created or "leaked."