c#多线程应用程序中的界面冻结
Posted
技术标签:
【中文标题】c#多线程应用程序中的界面冻结【英文标题】:interface freezes in c# multi-threaded app 【发布时间】:2011-01-13 17:45:59 【问题描述】:我有一个冻结界面的 c# .NET 多线程应用程序。不寻常的是,除非我让系统闲置足够长的时间以启动屏幕保护程序(这需要我重新输入密码才能重新获得对系统的访问权限),否则界面不会冻结。当界面再次可见时(在我成功输入密码后)所有窗口都是白色的。我可以看到窗口标题,移动窗口,最小化它们等等,但屏幕没有重新绘制。当我打破所有并进入调试器时,调用堆栈有 Application.Run()、外部代码,然后是“处于睡眠、等待或加入状态”。我在打开的所有四个线程中都设置了断点,它们仍在运行,只是主应用程序的 UI 线程被阻塞。当我查看我的线程列表时,我的主线程和我的四个工作线程现在由我的主线程和 11 个工作线程组成。我没有打开这么多线程,所以它必须是串口类。
现在让我描述一下我的程序。
我的主应用程序允许用户从串行端口收集和监控数据。我已经通过以下方式实现了这一点。当需要连接时,在主应用程序上按下一个按钮,该按钮调用 DLL 中的一个函数,该函数打开一个状态窗口,然后启动一个监视串行端口的线程。当该函数返回时,主应用程序会启动一个线程来监控 DLL 中在初始化时创建的队列。当从串行端口接收到数据时,会解析数据,然后更新状态窗口(通过委托)并将数据推送到队列中。当主应用程序工作线程在队列中看到数据时,它会检索它并使用委托将其发布到主应用程序的列表框中。在所有情况下,我都使用 BeginInvoke 来调用这些代表。
我的 DLL 包含两个库,用于它可以与之通信的两种不同类型的设备。
当我连接到两个设备时会出现此问题;因此每个设备有四个工作线程两个。 DLL 本身设置为 comm 对象,因此我可以从 C++/MFC 应用程序和 c# 应用程序轻松访问它,这两个应用程序都使用它。
我发现,如果我将代码添加到 DLL 内的线程,使其每 30 秒调用一次 Application.DoEvents(),接口将冻结大约 30 秒,然后像正常一样恢复活动。我认为某些东西阻塞了主线程并强制 DoEvents() 触发似乎打破了锁,但我不知道是什么导致了这个锁。这不是一个解决方案,只是一些有趣的事情。
如果您有任何建议,我将不胜感激。谢谢。
【问题讨论】:
Interface freezes in multi-threaded c# application的可能重复 【参考方案1】:我发现,如果我将代码添加到 DLL 内的线程,使其每 30 秒调用一次 Application.DoEvents(),接口将冻结大约 30 秒,然后像正常一样恢复活动。我认为某些东西阻塞了主线程并强制 DoEvents() 触发似乎打破了锁,但我不知道是什么导致了这个锁。这不是一个解决方案,只是一些有趣的事情。
我建议在新的 Visual Studio 2010 Concurrency Profiler 下运行您的程序。这将在运行时向您显示哪些线程被阻塞,以及它们正在等待哪些对象。线程争用已为您明确标记和突出显示。
您可以使用它轻松确定导致 UI 线程死锁的代码。
【讨论】:
这听起来很有希望。我只有2008,所以我想是时候升级了。谢谢。【参考方案2】:尝试将您的线程启动代码更改为Thread.Start()
而不是BeginInvoke()
。 BeginInvoke 不会将线程与您的 UI 分开,因为它可能会与 DoEvents 进行奇怪的交互。您可以在此处阅读 BeginInvoke 及其工作原理:http://www.codeproject.com/KB/cs/begininvoke.aspx
此外,DoEvents 在应用程序中从来都不是必需的,它会导致很多意外行为。使用包含在 Control.Invoke(...) 语句中的 UI 调用的线程。如果您使用的是 .NET 3.5+,则可以使用如下所示的委托来简化此操作:Invoke((Action)delegate() *code goes here*);
【讨论】:
我不使用 BeginInvoke 来启动我的线程,我使用它来调用委托函数。 +1 在这里,我昨天遇到了不同的 GUI 线程问题。从 BeginInvoke 切换到 Thread.Start() 是解决方案的一部分。最后一部分与启动期间线程间时间的细微差别有关。以上是关于c#多线程应用程序中的界面冻结的主要内容,如果未能解决你的问题,请参考以下文章