从System.Threading.Timer在UI中调用时如何避免泄漏句柄?

Dav*_*vy8 8 .net c# timer winforms handle-leak

似乎在System.Threading.Timer回调函数的回调中调用Winforms控件上的Invoke泄漏句柄,直到计时器被释放.有没有人知道如何解决这个问题?我需要每秒轮询一次值并相应地更新UI.

我在一个测试项目中尝试过,以确保这确实是泄漏的原因,这只是以下几点:

    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"));
    }
Run Code Online (Sandbox Code Playgroud)

如果您在Windows任务管理器中观看,这将泄漏2个句柄/秒.

Doc*_*Max 6

Invoke的行为类似于BeginInvoke/EndInvoke对,因为它将消息发布到UI线程,创建句柄,并等待该句柄以确定Invoked方法何时完成.这个句柄正在"泄漏".您可以看到这些是未命名的事件,使用Process Explorer在应用程序运行时监视句柄.

如果IASyncResult是IDisposable,则处理对象将负责清理句柄.事实并非如此,当垃圾收集器运行并调用IASyncResult对象的终结器时,句柄会被清理干净.你可以通过在每20次调用DoStuff后添加一个GC.Collect()来看到这一点 - 句柄计数每20秒就会下降一次.当然,通过添加对GC.Collect()的调用来"解决"问题是解决问题的错误方法.让垃圾收集器做自己的工作.

如果您不需要将Invoke调用设置为同步,请使用BeginInvoke而不是Invoke,并且不要调用EndInvoke; 最终结果将做同样的事情,但不会创建或"泄露"句柄.