UI Thread .Invoke()导致句柄泄漏?

在什么情况下,在使用委托和.InvokeRequired时,从非UI线程更新UI控件可能会导致进程的句柄不断增加?

例如:

 public delegate void DelegateUIUpdate(); private void UIUpdate() { if (someControl.InvokeRequired) { someControl.Invoke(new DelegateUIUpdate(UIUpdate)); return; } // do something with someControl } 

当在循环或定时器间隔中调用此方法时,程序的句柄会不断增加。

编辑:

如果以上内容被注释掉并修改如下:

 public delegate void DelegateUIUpdate(); private void UIUpdate() { //if (someControl.InvokeRequired) //{ // someControl.Invoke(new DelegateUIUpdate(UIUpdate)); // return; //} CheckForIllegalCrossThreadCalls = false; // do something with someControl } 

…然后句柄停止递增,当然我不想允许跨线程调用。

编辑2:

这是一个显示句柄增加的示例:

 Thread thread; private delegate void UpdateGUI(); bool UpdateTheGui = false; public Form1() { InitializeComponent(); thread = new Thread(new ThreadStart(MyThreadLoop)); thread.Start(); } private void MyThreadLoop() { while (true) { Thread.Sleep(500); if (UpdateTheGui) { UpdateTheGui = false; UpdateTheGuiNow(); } } } private void UpdateTheGuiNow() { if (label1.InvokeRequired) { label1.Invoke(new UpdateGUI(UpdateTheGuiNow)); return; } label1.Text = DateTime.Now.ToString("MM-dd-yyyy HH:mm:ss"); label2.Text = DateTime.Now.ToString("MM-dd-yyyy HH:mm:ss"); label3.Text = DateTime.Now.ToString("MM-dd-yyyy HH:mm:ss"); } private void btnInvoke_Click(object sender, EventArgs e) { UpdateTheGui = true; } 

Control.Invoke()方法不使用任何句柄。 但是,这个代码显然是从一个线程中调用的。 Thread 确实使用了句柄,其中有5个。

Thread类没有Dispose()方法,尽管它应该有一个。 这可能是设计的,对于线程池线程来说,很难可靠地调用,不可能这样。 终结器释放线程所需的5个句柄。 如果终结器永远不会运行,您的程序将需要不断增加的句柄数量。

没有让终结者运行是非常不寻常的。 你必须有一个启动大量线程但不分配大量内存的程序。 这往往只发生在静态测试中。 您可以使用Perfmon.exe诊断此情况,使用.NET内存性能计数器并检查是否正在生成gen#0集合。

如果在生产程序中发生这种情况,那么您必须自己调用GC.Collect()以避免失控的句柄泄漏。

我遇到了同样的问题

 this.Invoke(new DelegateClockUpdate(ChangeClock), sender, e); 

每次调用创建一个句柄。

句柄递增,因为Invoke是同步的,并且有效地将句柄悬挂。

应使用Wait Handle处理结果或Asynchronous BeginInvoke方法,如下所示。

 this.BeginInvoke(new DelegateClockUpdate(ChangeClock), sender, e); 

我在代码中看到了同样的东西。 我通过用BeginInvoke替换Invoke修复它。 手柄泄漏消失了。

多伦。

这是使用Invoke对UI线程进行编组更新的标准模式。

您确定问题不是由您的问题中未包含的应用程序中的其他代码引起的吗?

我不认为这是相关的。 也许只是等待垃圾收集器在Invoke()中部署新分配的对象。

我实际上看到了和JYelton一样的问题。 我在一个线程内有相同的调用来更新UI。

一行someControl.Invoke(new DelegateUIUpdate(UIUpdate)); 被称为,句柄增加一个。 在调用中肯定存在某种泄漏,但我不知道是什么导致它。 这已经在几个系统上得到validation。

具有显式句柄的Aync调用finalize。 Exapmle:

  public static class ActionExtensions { private static readonly ILog log = LogManager.GetLogger(typeof(ActionExtensions)); ///  /// Async exec action. ///  /// Action. public static void AsyncInvokeHandlers( this Action action) { if (action == null) { return; } foreach (Action handler in action.GetInvocationList()) { // Initiate the asychronous call. Include an AsyncCallback // delegate representing the callback method, and the data // needed to call EndInvoke. handler.BeginInvoke( ar => { try { // Retrieve the delegate. var handlerToFinalize = (Action)ar.AsyncState; // Call EndInvoke to free resources. handlerToFinalize.EndInvoke(ar); var handle = ar.AsyncWaitHandle; if (handle.SafeWaitHandle != null && !handle.SafeWaitHandle.IsInvalid && !handle.SafeWaitHandle.IsClosed) { ((IDisposable)handle).Dispose(); } } catch (Exception exception) { log.Error("Async Action exec error.", exception); } }, handler); } } } 

请参阅http://msdn.microsoft.com/en-us/library/system.iasyncresult.asyncwaithandle.aspx注意:

当您使用委托的BeginInvoke方法异步调用方法并从生成的IAsyncResult获取等待句柄时,我们建议您在完成使用后立即关闭等待句柄,方法是调用WaitHandle.Close方法。 如果您只是释放对等待句柄的所有引用,则在垃圾收集回收等待句柄时释放系统资源,但是当显式关闭或处置一次性对象时,垃圾收集工作效率更高。 有关更多信息,请参阅AsyncResult.AsyncWaitHandle属性。

这是一个扩展方法,其function与普通的Invoke调用类似,但会在以下情况后清理句柄:

 namespace ExtensionMethods { public static class ExtensionMethods { public static void InvokeAndClose(this Control self, MethodInvoker func) { IAsyncResult result = self.BeginInvoke(func); self.EndInvoke(result); result.AsyncWaitHandle.Close(); } } } 

然后,您可以像普通调用一样调用它:

 myForm.InvokeAndClose((MethodInvoker)delegate { someControl.Text = "New Value"; }); 

它将阻塞并等待委托执行,然后在返回之前关闭句柄。