似乎在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"));
}
如果你在Windows任务管理器中观看,这将泄漏2个句柄/秒。
答案 0 :(得分:6)
Invoke就像BeginInvoke / EndInvoke对一样,它将消息发布到UI线程,创建句柄,并等待该句柄以确定Invoked方法何时完成。这个句柄正在“泄漏”。您可以看到这些是未命名的事件,使用Process Explorer来监视应用程序运行时的句柄。
如果IASyncResult是IDisposable,则处理对象将负责清理句柄。事实并非如此,当垃圾收集器运行并调用IASyncResult对象的终结器时,句柄会被清理干净。你可以通过在每20次调用DoStuff后添加一个GC.Collect()来看到这一点 - 句柄计数每20秒就会下降一次。当然,通过添加对GC.Collect()的调用来“解决”问题是错误的解决问题的方法;让垃圾收集器自己完成工作。
如果您不需要将Invoke调用设置为同步,请使用BeginInvoke而不是Invoke,并且不要调用EndInvoke;最终结果将做同样的事情,但不会创建或“泄露”句柄。
答案 1 :(得分:2)
有什么理由你不能在这里使用System.Windows.Forms.Timer吗?如果计时器绑定到该表单,您甚至不需要调用。
答案 2 :(得分:2)
好吧,我给了它一点时间,看起来它实际上没有泄漏句柄,它只是垃圾收集器的不确定性。我每次嘀嗒嗒嗒嗒哒哒哒哒哒哒哒哒哒哒哒哒哒哒哒哒哒哒哒哒哒哒哒哒
为了确认我在每次回调时手动调用GC.Collect()的理论(不要在实际项目中这样做,这只是为了测试,那里有很多关于为什么这是个坏主意的文章)和句柄计数很稳定。
答案 3 :(得分:0)
有趣 - 这不是一个答案,但根据安德烈的评论,我会认为这不会以相同的方式泄漏处理,但它会以与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);
}