简体   繁体   English

查找哪个模块阻止了UI线程

[英]Find which module is blocking the UI thread

This is a two part question: 这是一个两部分问题:

  • I am working on a big project where multiple plugins developed by different teams are loaded inside one common container shell. 我正在做一个大项目,在这个项目中,由不同团队开发的多个插件被装入一个通用容器外壳中。 At times I can see that my UI updates are blocked as there are multiple parallel UI updates, i want to know if there is a way to find which component is blocking the ui thread 有时我会看到我的UI更新被阻止,因为有多个并行的UI更新,我想知道是否有办法找到哪个组件阻止了ui线程

  • In .net how can i create a separate UI thread which requires dedicated UI intensive work? 在.net中,我如何创建需要进行专门的UI密集工作的单独的UI线程?

Much Appreciate your help. 非常感谢您的帮助。 Thanks. 谢谢。

Use the debugger. 使用调试器。 Debug + Break All when you notice it blocking. 当您发现它阻塞时,请进行Debug + Break All。 Then Debug + Windows + Threads and select the main thread. 然后Debug + Windows + Threads并选择主线程。 The call stack window shows you what it is doing. 调用堆栈窗口将显示正在执行的操作。

A corner case is where these plugins are using a lot of calls to Control.Begin/Invoke or Dispatcher.Begin/Invoke. 一个极端的情况是,这些插件使用了大量对Control.Begin / Invoke或Dispatcher.Begin / Invoke的调用。 Your UI thread is not blocked in this case, it is just being overwhelmed by requests to dispatch the delegate targets. 在这种情况下,您的UI线程不会被阻塞,只是被派遣委托目标的请求所淹没。 And doesn't get around to doing its normal duties anymore, like repainting the windows and responding mouse and keyboard events. 而且不再能够正常工作,例如重新粉刷窗户以及响应鼠标和键盘事件。 There's little you can do about this beyond working with the plugin authors to get them to mend their ways. 除了与插件作者合作以使他们修补自己的方法外,您无能为力。

You've already got an UI thread, the thread that created the first window. 您已经有一个UI线程,该线程创建了第一个窗口。 Creating additional threads that have their own windows is possible but causes unsolvable problems with window Z-order (a window will disappear underneath the window of another app) and generous helpings of window interop threading misery. 创建具有自己窗口的其他线程是可能的,但是会导致Z轴顺序无法解决的问题(一个窗口将在另一个应用程序的窗口下方消失)以及大量的窗口互操作线程问题。

Visual Studio 2010's (in the higher SKUs) include features to check for this. Visual Studio 2010(在较高的SKU中)包含检查此功能。 If you run your program under the Concurrency Profiler, you can see exactly which threads are waiting on which locks when the deadlock occurs. 如果在并发分析器下运行程序,则可以确切地看到发生死锁时哪些线程正在等待哪些锁。 In addition, it will highlight the deadlock (I believe in bright red) to make it easy to track down. 此外,它将突出显示死锁(我相信是亮红色的),以便于跟踪。

One approach you can take (though it may require a bit of redesign) is to disallow all plugin logic from running in the UI thread. 您可以采取的一种方法(尽管可能需要重新设计)是禁止所有插件逻辑在UI线程中运行。 All operations that require updates to the UI must be routed through well-defined service interfaces that can interpret, dispatch and perhaps even throttle the UI updates. 所有需要更新UI的操作都必须通过定义良好的服务接口进行路由,这些服务接口可以解释,调度甚至限制UI更新。 This is only practical if your plugins are not deeply UI-centric and you have a service model that allows you to isolate the data being manipulated by the plugins from the visualization of that data. 仅当您的插件不是完全以UI为中心并且您具有一个服务模型,该服务模型可让您将插件正在操作的数据与该数据的可视化隔离开来时,这才是切实可行的。 Without knowing more about your application, I can't give more concrete recommendations. 在不了解您的应用程序的情况下,我无法给出更具体的建议。

Here are two possible solutions to the problem that I came up with quickly. 这是我很快想到的两个可能的解决方案。 I am sure there are other equally valid solutions though. 我相信还有其他同样有效的解决方案。

Option 1: Instead of using the push model (via the ISynchronizeInvoke methods) switch to a pull (or poll) model in which the UI queries the plugin for updates. 选项1:代替使用push模型(通过ISynchronizeInvoke方法),切换到pull(或轮询)模型,在该模型中,UI会向插件查询更新。 This has the following advantages. 这具有以下优点。

  • It breaks the tight coupling between the UI and worker/plugin threads that Control.Invoke imposes. 它打破了UI和Control.Invoke施加的工作线程/插件线程之间的紧密耦合。
  • It puts the responsibility of updating the UI thread on the UI thread where it should belong anyway. 它将更新UI线程的责任放在了它应该属于的UI线程上。
  • The UI thread gets to dictate when and how often the update should take place. UI线程可以指示更新的时间和频率。
  • There is no risk of the UI message pump being overrun as would be the case with the marshaling techniques initiated by the worker/plugin thread. UI消息泵不会像工作线程/插件线程启动的封送处理技术那样被超限运行。
  • The worker/plugin thread does not have to wait for an acknowledgement that the update was performed before proceeding with its next steps (ie. you get more throughput on both the UI and worker/plugin threads). 辅助线程/插件线程不必等待确认已执行更新,然后再继续执行下一步(即,UI和辅助线程/插件线程都可获得更高的吞吐量)。

Option 2: Have the plugin accept an ISynchronizeInvoke instance instead of an actual Form or Control . 选项2:让插件接受ISynchronizeInvoke实例,而不是实际的FormControl This special synchronizing object will be implemented using a dedicated thread and a queue that acts as buffer between the plugin and the UI. 将使用专用线程和充当插件与UI之间的缓冲区的队列来实现此特殊的同步对象。 It will accept update messages via the normal Invoke or BeginInvoke methods, which means you can keep the plugin architecture and interfaces mostly intact, and then forward those messages on to the UI after some type of filtering, merging, and throttling operations have occurred. 它将通过普通的InvokeBeginInvoke方法接受更新消息,这意味着您可以使插件体系结构和接口大部分保持完整,然后在进行某种类型的过滤,合并和限制操作后将这些消息转发到UI。 The number of update messages existing in the synchronizing object will ebb and flow as the UI and plugin threads work load changes. 随着UI和插件线程工作负载的变化,同步对象中存在的更新消息的数量会起伏不定。 It could be smart enough to change its forwarding strategy as the rate of messages increase. 随着消息速率的提高,它可能会足够聪明地更改其转发策略。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM