Control.Invoke() 挂起应用程序

12

我的控件在加载数据时会显示动画,当线程完成后,我会隐藏动画并显示控件。所以我正在从一个线程中执行以下代码:

protected void InvokeEnableBackControl()
{
    if (this.InvokeRequired)
    {                
        this.Invoke(new OpHandler(EnableBackControl));
    }
    else
    {
        EnableBackControl();
    }
}

有时候当我执行这段代码时,主线程会在以下代码中卡住:

protected virtual void EnableBackControl()
{
    if (overlayAnimation.TargetControl != null)
    {
        overlayAnimation.TargetControl.BringToFront();
    }

    overlayAnimation.SendToBack();
    overlayAnimation.Enabled = false;
    overlayAnimation.Visible = false;                      

我不确定是设置Enable属性还是Visible属性导致了程序卡住。您知道哪些情况可能会导致在Control.Invoke中调用这些属性时程序卡住吗?


在它卡住的时候,是否有明显的模式可辨? - Justin Morgan
不,只有“有时候”。很烦人。 - Daniel Peñalba
记录程序挂在哪个语句上,发布堆栈跟踪。 - Hans Passant
5个回答

27

8

我以前在执行后台线程上的.Invoke时遇到过问题,而我的主线程仍然很忙 - 这会给人留下应用程序已挂起的印象,因为.Invoke只是坐在那里等待主线程响应它正在关注的内容。可能的原因:

  • 您的主线程被阻塞等待某些东西
  • 您的主窗体当前有一个模态对话框,因此它不会听取新的请求
  • 您的主线程正在旋转,要么不断检查某个任务是否完成,要么做新的工作。在我的情况下,主线程在紧密循环中花费了第一分钟来启动后台线程,因此它没有监听来自后台线程的任何.Invoke请求。

当您附加调试器时,请特别注意您的主控件MessagePump线程正在做什么 - 我怀疑它缺乏注意力是您麻烦的原因。如果您确定主线程中的紧密循环未响应,请尝试在循环中插入 .DoEvents ,这将暂停执行并强制主线程清空消息队列并处理任何未完成的请求。


1
谢谢,那正是我的情况,Application.DoEvents()解决了问题。 - Tate

4

在Visual Studio中以调试模式运行应用程序,让其挂起,然后暂停调试并检查线程。


1
这是什么样的答案?我使用调试器附加了进程,暂停并检查了线程。这就是我知道该线程在第一个代码块中,而主线程在第二个代码块中的原因。 - Daniel Peñalba
@Daniel Peñalba 这是一种帮助我的解决方案。因为当你这样做时,你可以看到线程停止的确切行。 - Andrey
我已经这样做了。当我中断所有线程,突出显示的行是overlayAnimation.Visible = false;但通常Visual Studio(几乎2003)会停在前一行。只有有时,这就是我不确定是那一行还是前一行的原因。 - Daniel Peñalba

0

在编程中,你必须使用BeginInvoke而不是Invoke,请参考链接


0
我发现实际绘制/绘画控件可能会非常慢,特别是如果您有很多控件和/或使用双缓冲进行平滑刷新。我正在使用BeginInvoke从套接字接收的数据更新listview控件。有时更新发生得如此之快,以至于它会冻结应用程序。我通过将套接字异步接收到的所有内容都写入队列中,并在单独的线程中出队数据并在listview上使用BeginUpdateEndUpdate执行所有未完成的更新来解决了这个问题。这消除了大量额外的重绘,并使应用程序更加响应。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接