假设您永久使用
异步调用UI线程/调度程序上的方法while (true) {
uiDispatcher.BeginInvoke(new Action<int, T>(insert_), DispatcherPriority.Normal, new object[] { });
}
在程序的每次运行中,您都会发现应用程序的GUI在大约90秒后由于调用的泛滥而开始冻结(时间变化但大约在1到2分钟之间)。
为了足够早地阻止它,怎么能准确地确定(测量?)重载的点?
附录I:
在我的实际程序中,我没有无限循环。我有一个算法,在终止之前迭代了几百次。在每次迭代中,我都在我的WPF应用程序中向List控件添加一个字符串。我使用了while(true){...}构造,因为它匹配最佳情况。事实上,算法正确终止,并且所有(数百个)字符串都正确地添加到我的列表中但是在一段时间之后我失去了使用GUI的能力,直到算法终止 - 然后GUI再次响应。
附录II:
我的程序的目的是在运行时观察特定的算法。我添加的字符串是日志条目:每次迭代一个日志字符串。我调用这些add-operations的原因是该算法在另一个线程中运行,而不是UI线程。为了赶上我不能从UI线程以外的任何线程进行UI操作的事实我构建了某种ThreadSafeObservableCollection(但我很确定这段代码不值得发布,因为它会减损实际问题什么我认为UI无法处理方法的重复和快速调用。
答案 0 :(得分:2)
这很直截了当:当你超负荷用户的眼球时,你做错了。就现代cpu内核而言,这种情况发生得非常快,超过每秒20次更新,显示的信息看起来就像模糊。电影利用的东西,电影以每秒24帧的速度播放。
以更快的速度更新只是浪费资源。在UI线程开始弯曲之前,您仍然有大量的喘息空间。这取决于你要求它做的工作量,但典型的是x50安全边际。基于Environment.TickCount的简单计时器将完成工作,当差异> = 45毫秒时触发更新。
答案 1 :(得分:2)
经常向UI发布是一个红旗。这是另一种选择:将新字符串放入ConcurrentQueue
并让计时器每隔100毫秒将它们拉出来。
非常简单易行,结果非常完美。
答案 2 :(得分:1)
好的,对不起评论中的错误链接,但我一直在阅读,也许这会有所帮助:
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.
也许你可以对你正在等待的代表人数做些什么......
答案 3 :(得分:1)
我没有使用WPF - 只是Windows Forms,但我建议如果只有一个只能查看的控件需要异步更新,那么正确的方法就是编写控件以便它可以从任何线程自由访问属性,只有当还没有更新待处理时才更新控件BeginInvoke
刷新例程;后一个确定可以使用Int32
“flag”和Interlock.Exchange
(属性setter在更改底层字段后调用Interlocked.Exchange
;如果标志已清除,则确实刷新例程中的BeginInvoke
;刷新例程然后清除标志并执行刷新)。在某些情况下,可以通过让控件的刷新例程检查自上次运行以来经过了多长时间来进一步增强模式,如果答案小于20ms左右,则使用计时器在20ms后触发刷新前一个。
尽管.net可以处理在UI线程上发布的许多BeginInvoke
个动作,但对于一次暂停的单个控件的更新通常是没有意义的。将待处理操作限制为每个控件一个(或最多一个小数字),并且不存在队列溢出的危险。
答案 4 :(得分:1)
要以更类似WPF的方式放置supercat的解决方案,尝试MVVM模式然后你可以有一个单独的视图模型类,你可以在线程之间共享,也许在适当的点上锁定或使用并发集合类。你实现了一个接口(我认为它是INotifyPropertyChanged并触发一个事件来说集合已经改变。这个事件必须从UI线程触发,但只需要
答案 5 :(得分:0)
在完成其他人提供的答案以及您对他们的评论后,您的实际意图似乎是确保UI保持响应。为此我认为你已经收到了很好的建议。
但是,为了回答你的问题(如何检测和标记UI线程的重载),我可以建议如下:
虽然我承认,这不是万无一失的,但通过实证调整你的“措施”,你应该能够在影响你之前发现超载。
答案 6 :(得分:0)
使用StopWatch来衡量最短,最长,平均,第一次和最后一次更新持续时间。 (您可以将其输出到您的UI。)
您的更新频率必须<&lt;比1 /(平均更新持续时间)。
更改算法的实现,以便多媒体计时器调用迭代,例如this .NET wrapper或this .NET wrapper。当计时器被激活时,使用Interlocked来防止在当前迭代完成之前运行新的迭代。如果需要在main上进行迭代,请使用调度程序。您可以为每个计时器事件运行多次迭代,为此使用参数,并与时间测量一起确定每个计时器事件运行的进程数以及您希望计时器事件的频率。
我建议不要使用小于5毫秒的定时器,因为定时器事件会使CPU窒息。
正如我在评论中写的更早,在调度到主线程时使用DispatcherPriority.Input,这样,UI的CPU时间不会被调度窒息。这与UI消息具有相同的优先级,因此不会忽略它们。