【发布时间】:2010-11-02 15:54:36
【问题描述】:
这是一个两部分的问题:
我正在处理一个大型项目,其中由不同团队开发的多个插件被加载到一个通用容器外壳中。有时我可以看到我的 UI 更新被阻止,因为有多个并行 UI 更新,我想知道是否有办法找到哪个组件阻止了 ui 线程
在 .net 中如何创建需要专门的 UI 密集型工作的单独 UI 线程?
非常感谢您的帮助。谢谢。
【问题讨论】:
标签: c# .net multithreading user-interface
这是一个两部分的问题:
我正在处理一个大型项目,其中由不同团队开发的多个插件被加载到一个通用容器外壳中。有时我可以看到我的 UI 更新被阻止,因为有多个并行 UI 更新,我想知道是否有办法找到哪个组件阻止了 ui 线程
在 .net 中如何创建需要专门的 UI 密集型工作的单独 UI 线程?
非常感谢您的帮助。谢谢。
【问题讨论】:
标签: c# .net multithreading user-interface
使用调试器。当您发现它阻塞时,调试+全部中断。然后 Debug + Windows + Threads 并选择主线程。调用堆栈窗口会显示它正在做什么。
一个极端情况是这些插件使用大量对 Control.Begin/Invoke 或 Dispatcher.Begin/Invoke 的调用。在这种情况下,您的 UI 线程不会被阻塞,它只是被调度委托目标的请求所淹没。并且不再执行其正常职责,例如重新绘制窗口和响应鼠标和键盘事件。除了与插件作者合作让他们改过自新之外,您几乎无能为力。
您已经有了一个 UI 线程,即创建第一个窗口的线程。创建具有自己的窗口的其他线程是可能的,但会导致窗口 Z 顺序无法解决的问题(一个窗口将在另一个应用程序的窗口下方消失)以及窗口互操作线程痛苦的慷慨帮助。
【讨论】:
Visual Studio 2010(在更高版本的 SKU 中)包含检查这一点的功能。如果您在 Concurrency Profiler 下运行程序,您可以准确地看到发生死锁时哪些线程正在等待哪些锁。另外,它会突出显示死锁(我相信是鲜红色),以便于追踪。
【讨论】:
您可以采取的一种方法(尽管可能需要进行一些重新设计)是禁止所有插件逻辑在 UI 线程中运行。所有需要更新 UI 的操作都必须通过定义明确的服务接口进行路由,这些接口可以解释、调度甚至限制 UI 更新。仅当您的插件不是以 UI 为中心并且您有一个服务模型允许您将插件操作的数据与该数据的可视化隔离时,这才是实用的。在不了解您的应用程序的情况下,我无法给出更具体的建议。
【讨论】:
以下是我快速提出的两个可能的解决方案。我相信还有其他同样有效的解决方案。
选项 1: 不使用推送模型(通过ISynchronizeInvoke 方法),而是切换到 UI 查询插件以获取更新的拉取(或轮询)模型。这具有以下优点。
Control.Invoke 强加的 UI 和工作线程/插件线程之间的紧密耦合。选项 2: 让插件接受 ISynchronizeInvoke 实例,而不是实际的 Form 或 Control。这个特殊的同步对象将使用一个专用线程和一个充当插件和 UI 之间缓冲区的队列来实现。它将通过普通的Invoke 或BeginInvoke 方法接受更新消息,这意味着您可以保持插件架构和接口基本完整,然后在某种类型的过滤、合并和限制操作后将这些消息转发到 UI已经发生。同步对象中存在的更新消息的数量将随着 UI 和插件线程工作负载的变化而起起落落。随着消息速率的增加,它可能足够聪明地改变其转发策略。
【讨论】: