我在自定义EventHandler
上有一个Control
,它从后台线程调用并使控件无效。为此,我使用Invoke
,如下所示:
void Foo::handleProgressChanged(Object^ sender, EventArgs^ args)
{
if (InvokeRequired)
{
Invoke(gcnew EventHandler(this, &Foo::handleProgressChanged), sender, args);
return;
}
this->UpdateProgress();
this->Invalidate(true);
}
在某些罕见的情况下,对Invoke
的调用会导致死锁,尽管消息循环应该是抽水的。主线程的callstack是:
ntdll.dll!770c5ca4()
[Frames below may be incorrect and/or missing, no symbols loaded for ntdll.dll]
user32.dll!75e6073f()
System.Windows.Forms.dll!System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(int dwComponentID, int reason = -1, int pvLoopData = 0) + 0x3c8 bytes
System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(int reason = -1, System.Windows.Forms.ApplicationContext context = {Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.WinFormsAppContext}) + 0x177 bytes
System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoop(int reason, System.Windows.Forms.ApplicationContext context) + 0x61 bytes
System.Windows.Forms.dll!System.Windows.Forms.Application.Run(System.Windows.Forms.ApplicationContext context) + 0x18 bytes
Microsoft.VisualBasic.dll!Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.OnRun() Line 768 + 0x9 bytes Basic
Microsoft.VisualBasic.dll!Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.DoApplicationModel() Line 1444 Basic
Microsoft.VisualBasic.dll!Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.Run(String() commandLine = Nothing) Line 491 + 0x7 bytes Basic
...
我尝试切换到BeginInvoke
,但传递的委托从未执行过。我找到了一个远非最优的解决方法,我认为这是一个邪恶的黑客攻击:如果我两次调用BeginInvoke
,第二个调用会产生预期效果:
void Foo::handleProgressChanged(Object^ sender, EventArgs^ args)
{
if (InvokeRequired)
{
IAsyncResult^ r = BeginInvoke(gcnew EventHandler(this, &Foo::handleProgressChanged), sender, args);
if (!r->AsyncWaitHandle->WaitOne(1000, false))
{
r = BeginInvoke(gcnew EventHandler(this, &Foo::handleProgressChanged), sender, args);
}
}
this->UpdateProgress();
this->Invalidate(true);
}
我无法将代码简化为一个小的自包含示例,但我的具体问题是:
Invoke
阻止后台线程的条件下,何时
UI线程是空闲的? BeginInvoke
成功,而第一次不成功? 我正在使用.NET Framework 2和C ++ / CLI,但我认为问题不是特定于语言的。
修改
更清楚:原始方法大部分时间都有效。会话中的第一个事件按预期处理。但是后来,特别是当事件经常发生时,它会陷入僵局。