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;
}
In what circumstances would updating a UI control from a non-UI thread could cause the processes' handles to continually increase, when using a delegate and .InvokeRequired
?
For example:
public delegate void DelegateUIUpdate();
private void UIUpdate()
{
if (someControl.InvokeRequired)
{
someControl.Invoke(new DelegateUIUpdate(UIUpdate));
return;
}
// do something with someControl
}
When this is called in a loop or on timer intervals, the handles for the program consistently increase.
EDIT:
If the above is commented out and amended as such:
public delegate void DelegateUIUpdate();
private void UIUpdate()
{
//if (someControl.InvokeRequired)
//{
// someControl.Invoke(new DelegateUIUpdate(UIUpdate));
// return;
//}
CheckForIllegalCrossThreadCalls = false;
// do something with someControl
}
...then the handles stop incrementing, however I don't want to allow cross thread calls, of course.
EDIT 2:
Here is a sample that shows the handles increase:
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;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
时遇到了同样的问题
我在每次调用创建一个句柄
。句柄会递增,因为 Invoke 是同步的,并且句柄实际上已保持挂起状态。
应该使用等待句柄来处理结果,或者使用异步 BeginInvoke 方法,如下所示。
I had the same problem with
creating one handle each call.
The handle increments because Invoke is Synchronous and effectively the handle has been left hanging.
Either a Wait Handle should be used to process the result or the Asynchronous BeginInvoke method as shown below.
Control.Invoke() 方法不消耗任何句柄。然而,这段代码显然是从线程中调用的。线程确实消耗句柄,其中有5个。
Thread 类没有 Dispose() 方法,尽管它应该有一个方法。这可能是设计使然,对于线程池线程来说,可靠地调用是非常困难的,这是不可能的。线程所需的 5 个句柄由终结器释放。如果终结器从不运行,您的程序将需要不断增加的句柄数量。
不让终结器运行是很不寻常的。您必须有一个启动大量线程但不分配大量内存的程序。这往往只发生在静态测试中。您可以使用 Perfmon.exe 诊断此情况,使用 .NET 内存性能计数器并检查第 0 代收集是否正在完成。
如果这种情况发生在生产程序中,那么您必须自己调用 GC.Collect() 以避免失控句柄泄漏。
The Control.Invoke() method doesn't consume any handles. However, this code is clearly called from a thread. A Thread does consume handles, 5 of them.
The Thread class doesn't have a Dispose() method, although it ought to have one. That was probably by design, it would be very difficult to call reliably, impossibly so for threadpool threads. The 5 handles that a thread requires are released by the finalizer. Your program will require ever increasing amounts of handles if the finalizer never runs.
Not getting the finalizer to run is quite unusual. You would have to have a program that starts a lot of threads but doesn't allocate a lot of memory. This tends to only happen in static tests. You can diagnose this condition with Perfmon.exe, use the .NET memory performance counters and check if gen #0 collections are being done.
If this happens in a production program then you'll have to call GC.Collect() yourself to avoid a runaway handle leak.
我在我的代码中看到了同样的事情。我通过将
Invoke
替换为BeginInvoke
来修复此问题。手柄泄漏消失了。多伦.
I've seen the same thing in my code. I fixed it by replacing
Invoke
withBeginInvoke
. The handle leak went away.Doron.
我实际上看到与 JYelton 发生同样的问题。我在线程内进行了相同的调用来更新 UI。
一旦调用
someControl.Invoke(new DelegateUIUpdate(UIUpdate));
行,句柄就会增加 1。调用中肯定存在某种泄漏,但我不知道是什么原因造成的。这已在多个系统上得到验证。I actually see the same problem occuring as JYelton. I have the same call from within a thread to update the UI.
As soon as the line
someControl.Invoke(new DelegateUIUpdate(UIUpdate));
is called, the handle increases by one. There is certainly a leak of some kind on the invoke, but I have no idea what is causing it. This has been verified on several systems.具有显式句柄终结的 Aync 调用。示例:
请参阅 http://msdn.microsoft.com/en- us/library/system.iasyncresult.asyncwaithandle.aspx 注意:
Aync call with explicit handle finalize. Exapmle:
See http://msdn.microsoft.com/en-us/library/system.iasyncresult.asyncwaithandle.aspx note:
这是一个扩展方法,其功能与普通 Invoke 调用类似,但会在之后清理句柄:
然后您可以像普通调用一样调用它:
它将阻塞并等待委托执行,然后在返回之前关闭句柄。
Here's an extension method which functions similarly to the normal Invoke call, but will clean up the handle after:
You can then call it very similarly to a normal invoke:
It will block and wait for the delegate to execute, then close the handle before returning.
这是使用
Invoke
将更新编组到 UI 线程的标准模式。您确定您的问题不是由应用程序中未包含在您的问题中的其他代码引起的吗?
This is the standard pattern for using
Invoke
to marshall updates to the UI thread.Are you sure your problem is not being caused by other code in your application that is not included in your question?
我认为这没有关系。也许只是等待垃圾收集器在 Invoke() 内处理新分配的对象。
I don't think it is related. Perhaps just waiting for the garbage collector to dispose the newly allocated object(s) inside Invoke().