-->

当该GUI超载?(When is the GUI overloaded?)

2019-09-19 02:20发布

假设你永久异步调用的方法到UI线程/调度员

while (true) {

    uiDispatcher.BeginInvoke(new Action<int, T>(insert_), DispatcherPriority.Normal, new object[] { });
}

在程序的每个来看,你观察到的应用程序的GUI开始约90秒后,冻结由于调用的洪水(时间不同,但1到2分钟的谎言大约)。

一个人怎么能准确地确定(测量?)点时,该超载发生,以阻止它足够早?

附录I:

在我实际的程序我没有一个无限循环。 我有一个算法,终止之前迭代数百倍。 在每次迭代我添加一个字符串,在我的WPF应用程序列表控制。 我用了,而(真){...}构造,因为它最匹配会发生什么。 事实上,正确的算法,并终止所有(数百)字符串正确添加到我的清单,但一段时间后,我失去用我的GUI,直到算法终止的能力 - 那么GUI响应一次。

附件二:

我的计划的目的是在它的运行来观察一个特定的算法。 我加入的字符串是日志条目:每次迭代一个日志字符串。 为什么我调用这些附加操作的原因是,该算法是在另一个线程比UI线程中运行。 为了赶上的事实,我不能比UI线程我建立某种ThreadSafeObservableCollection其他任何线程做UI操作(但我敢肯定,这个代码是不值得发布,因为它会与实际问题减损什么我想的是,UI无法处理方法的反复和快速调用。

Answer 1:

这是非常简单的:你做错了由你超载用户眼球的时间。 这恰好很快就现代CPU内核而言,超过每秒20次更新所显示的信息只是开始看起来像一个模糊。 一些电影院利用的,电影以每秒24帧播放。

更新任何比这更快的仅仅是一个资源的浪费。 你还有UI线程开始前扣喘息空间的大量离开。 这取决于你问它做的工作量,但典型的是X50的安全边际。 基于Environment.TickCount一个简单的定时器将完成这项工作,当差值为> = 45毫秒触发一个更新。



Answer 2:

发布经常到UI是红旗。 这是一种替代方案:将新的字符串成ConcurrentQueue并有一个计时器拉出来每100ms。

很简单,易于实现,结果是完美的。



Answer 3:

我没有用WPF - 只是Windows窗体,但我建议,如果有这将需要异步更新视图只控制,这样做的正确方法是编写控制,使得其性能可从任何线程自由访问,以及更新控制将BeginInvoke 的刷新程序; 后者确定可以用制成Int32 “标记”和Interlock.Exchange (属性setter调用Interlocked.Exchange改变底层字段后的标志;如果该标志已经清楚,但它确实一个BeginInvoke上刷新程序;该刷新程序然后清除标志并执行刷新)。 在某些情况下,该模式还可以通过其控制的刷新例行检查了多少时间自上次运行经过,如果答案是小于20ms左右的增强,使用定时器触发后刷新20ms的前一个。

尽管.NET可以处理有许多BeginInvoke贴在UI线程上的行动,它往往是毫无意义的比更新更单个控制在一个时间待定。 限制挂起的操作来控制每一个(或至多少量的),并且将没有队列溢出的危险。



Answer 4:

好吧,抱歉的意见坏链接之前,但我一直在读书,也许这将有所帮助:

The DispatcherOperation object returned by BeginInvoke can be used in several ways to interact with the specified delegate, such as:

Changing the DispatcherPriority of the delegate as it is pending execution in the event queue.
Removing the delegate from the event queue.
Waiting for the delegate to return.
Obtaining the value that the delegate returns after it is executed.

If multiple BeginInvoke calls are made at the same DispatcherPriority, they will be executed in the order the calls were made.

If BeginInvoke is called on a Dispatcher which has shut down, the status property of the returned DispatcherOperation is set to Aborted.

也许你可以做你正在等待代表人数的东西...



Answer 5:

为了把supercat的解决方案,像这样一个更WPF,尝试的MVVM模式,那么你可以有你可以在线程之间共享一个单独的视图模型类,可能采取锁了在apropriate点或使用并发集合类。 您实现一个接口(我认为这是INotifyPropertyChanged的和火灾事件说集合了变化。这种情况必须从UI线程被解雇,但只需要



Answer 6:

通过他人对他们您的意见提供的答案去后,你的真实意图似乎是确保用户界面保持响应。 为此,我想你已经收到了很好的建议。

但尽管如此,回答你的问题(如何检测和UI线程的标志超载)一字不差,我可以建议如下:

  1. 首先确定哪些应该是“超载”的定义(例如,我可以假设它是“UI线程停止渲染控制和停止处理用户输入”为一个足够大的持续时间)
  2. 定义这个持续时间(例如,如果UI线程继续处理渲染和在最40ms的我会说这是不超载输入消息)。
  3. 现在启动一个DispactherTimer用的DispatcherPriority按照自己的定义设置超载(我例如,它可以DispatcherPriority.Input或更低)和间隔时间比你的“时间”要足够小超载
  4. 保持DateTime类型的共享变量和计时器的每个刻度改变它的值,以DateTime.Now。
  5. 在传递给BeginInvoke的委托,就可以计算出当前时间和最后一个时间标记被解雇之间的差异。 如果超过你的超载以及随后根据您的定义UI线程是“重载”的“措施”。 然后,您可以设置它可以从你的循环内进行检查,以采取适当行动的共同标志。

虽然我承认,这是骗不了的证据,但根据经验调整你的“措施”,你应该能够探测到它之前,你的影响超载。



Answer 7:

使用一个秒表来测量最大值,最小值,平均值,第一个和最后更新的持续时间。 (您可以输出中给你的用户界面。)

您的更新频率必须<大于1 /(平均更新持续时间)。

更改算法的实现,使其迭代通过多媒体计时器如调用此.NET包装或此.NET包装 。 当计时器被激活时,使用互锁以防止运行一个新的循环之前,当前迭代完成。 如果您需要迭代主,使用调度。 您可以运行每个定时器事件超过1次迭代,使用参数,这和连同时间测量确定有多少interations每个计时器事件,以及如何往往你希望定时器事件来运行。

我不建议使用超过5毫秒少定时器,因为计时器事件会窒息的CPU。

正如我在评论中写道ealier,使用DispatcherPriority.Input分派到主线程的时候,这样的UI的CPU时间不被派遣窒息。 这是相同的优先级的UI消息也有,所以这样,他们没有忽略。



文章来源: When is the GUI overloaded?