代码之家  ›  专栏  ›  技术社区  ›  Davy8

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

  •  8
  • Davy8  · 技术社区  · 16 年前

    在System.Threading.Timer的回调中调用winforms控件似乎会泄漏句柄,直到释放计时器为止。有人知道如何解决这个问题吗?我需要每秒轮询一个值,并相应地更新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"));
        }
    

    如果在windows任务管理器中进行监视,每秒将泄漏2个句柄。

    4 回复  |  直到 16 年前
        1
  •  6
  •   DocMax    16 年前

    Invoke的作用类似于BeginInvoke/EndInvoke对,它将消息发布到UI线程,创建句柄,并等待该句柄来确定调用的方法何时完成。正是这个句柄在“泄漏”。您可以使用 Process Explorer

    如果IASyncResult是可识别的,则处理该对象将负责清理句柄。事实并非如此,当垃圾收集器运行并调用IASyncResult对象的终结器时,句柄将被清除。您可以通过在每20次对DoStuff的调用后添加一个GC.Collect()看到这一点——句柄计数每20秒下降一次。当然,通过添加对GC.Collect()的调用来“解决”问题是最重要的 错误的 解决问题的方法;让垃圾收集器完成自己的工作。

    需要

        2
  •  2
  •   Joshua    16 年前

    有什么原因不能在此处使用System.Windows.Forms.Timer吗?如果计时器绑定到该窗体,则您甚至不需要调用它。

        3
  •  2
  •   Davy8    16 年前

    好吧,我给它多一点时间,它看起来实际上并没有泄漏句柄,这只是垃圾收集器的不确定性质。我把它撞到了每滴答10毫秒的高度,它会爬得很快,30秒后又会掉下来。

    为了证实我在每次回调时手动调用GC.Collect()的理论(不要在实际项目中这样做,这只是为了测试,有很多文章都在讨论为什么这是个坏主意),句柄计数是稳定的。

        4
  •  0
  •   Wil P    16 年前

    有趣-这不是一个答案,但根据Andrei的评论,我认为这不会以同样的方式泄漏处理,但它确实以OP提到的相同速率泄漏处理。

    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);
        }