How to avoid leaking handles when invoking in UI from System.Threading.Timer?

↘锁芯ラ 提交于 2019-11-30 19:34:24

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."

Is there some reason you cannot use a System.Windows.Forms.Timer here? If the timer is bound to that form you won't even need to invoke.

Okay, I gave it a little more time and it looks like it's actually not leaking handles, it's just the indeterminate nature of the garbage collector. I bumped it way up to 10ms per tick and it'd climb up really fast and 30 seconds later would drop back down.

TO confirm the theory I manually called GC.Collect() on each callback (Don't do this in real projects, this was just to test, there's numerous articles out there about why it's a bad idea) and the handle count was stable.

Interesting - This is not an answer but based on Andrei's comment I would have thought this would not leak handles the same way however it does leak handles at the same rate the OP mentioned.

System.Threading.Timer timer;
    public Form2()
    {
        InitializeComponent();

    }

    private void UpdateFormTextCallback()
    {
        this.Text = "Hello World!";
    }

    private Action UpdateFormText;

    private void DoStuff(object value)
    {
        this.Invoke(UpdateFormText);
    }

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        timer = new System.Threading.Timer(new TimerCallback(DoStuff), null, 0, 500);
        UpdateFormText = new Action(UpdateFormTextCallback);
    }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!