WPF 应用程序问题和 GUI 响应缓慢
我一直在分析一个 WPF 应用程序,它基本上从服务器获取数据并在 GUI 中显示数据。
这段代码不是我的,并且该应用程序存在与 GUI 响应缓慢相关的问题,我正在尝试查找该问题的原因。
我想与您分享我对问题可能出在哪里的想法,我想听听您对此有何看法,无论它是否有意义。
为了从服务器获取数据,应用程序使用 7 个线程(这样做主要是因为应用程序逻辑,所以不要过多关注为什么是 7 个线程而不仅仅是 1 个...),现在,每个线程都是通过调用一个名为 CreateThreadForTask() 的方法来创建的。
public void StartAllThreads()
{
this.CreateThreadForTask(Tasks.Task1);
this.CreateThreadForTask(Tasks.Task2);
this.CreateThreadForTask(Tasks.Task3);
this.CreateThreadForTask(Tasks.Task4);
this.CreateThreadForTask(Tasks.Task5);
this.CreateThreadForTask(Tasks.Task6);
this.CreateThreadForTask(Tasks.Task7);
}
public void CreateThreadForTask(Tasks task)
{
... // this part of the code is not important
//! Initialize and start timer
timer = null;
timer = new DispatcherTimer();
timer.Tick += new EventHandler(RunMainSyncForTask);
timer.Start();
}
public void RunMainSyncForTask(object s, EventArgs e)
{
int sec = int.Parse(AppSettings.GetSetting("syncInterval"));
timer.Interval = new TimeSpan(0, 0, sec);
//threadCaller is a background worker
threadCaller = InitializeThread();
threadCaller.DoWork += DoWorkEventHandler(StartSync);
threadCaller.RunWorkerAsync();
}
当我调试代码时,我注意到所有线程都是使用 DispatcherTimer 创建的;我认为应用程序正在创建 7 个 DispatcherTimer,并将计时器的 Tick 事件与 RunMainSyncForTask() 方法链接起来,该方法在内部创建一个后台工作程序,从服务器获取数据并将该数据保存到本地数据库。
现在,这是从 MSDN 获取的
DispatcherTimer 在每个 Dispatcher 循环的顶部重新计算。
定时器不保证在时间间隔发生时准确执行,但保证在时间间隔发生之前不执行。这是因为 DispatcherTimer 操作像其他操作一样被放置在 Dispatcher 队列中。 DispatcherTimer 操作何时执行取决于队列中的其他作业及其优先级。
因此,基于此,我相信每次计时器执行滴答事件时,应用程序都会向线程发送垃圾邮件,并且这会同时执行 7 次;由于 DispatcherTimer 的性质,所有这些操作都被添加到 Dispatcher 队列中,这使得 GUI 响应变慢,因为 Dispatcher 正忙。
另外,应用程序的另一个问题是,当它运行时,它会占用大约 90-95% 的 CPU,我认为如果我的假设是正确的,这也可能是这个问题的原因。
因此,如果您能分享一些有关此内容的内部信息,我将不胜感激。
谢谢。
I've been analyzing a WPF application which basically fetch data from a server and display the data in the GUI.
This code is not mine, and the application has a problem related with slow response from the GUI, I'm trying to find the reason for that problem.
I want to share with you my idea of which the problem could be and I'll like to hear what do you think about it, whether it makes any sense or not.
To get the data from the server, the application is using 7 threads (this is done in this way mostly because of the application logic, so don't pay too much attention to why 7 and not just one...), now, each thread is created by calling a method, called CreateThreadForTask()
public void StartAllThreads()
{
this.CreateThreadForTask(Tasks.Task1);
this.CreateThreadForTask(Tasks.Task2);
this.CreateThreadForTask(Tasks.Task3);
this.CreateThreadForTask(Tasks.Task4);
this.CreateThreadForTask(Tasks.Task5);
this.CreateThreadForTask(Tasks.Task6);
this.CreateThreadForTask(Tasks.Task7);
}
public void CreateThreadForTask(Tasks task)
{
... // this part of the code is not important
//! Initialize and start timer
timer = null;
timer = new DispatcherTimer();
timer.Tick += new EventHandler(RunMainSyncForTask);
timer.Start();
}
public void RunMainSyncForTask(object s, EventArgs e)
{
int sec = int.Parse(AppSettings.GetSetting("syncInterval"));
timer.Interval = new TimeSpan(0, 0, sec);
//threadCaller is a background worker
threadCaller = InitializeThread();
threadCaller.DoWork += DoWorkEventHandler(StartSync);
threadCaller.RunWorkerAsync();
}
When I was debugging the code I noticed that all the threads are created using a DispatcherTimer; what I think is that the application is creating 7 DispatcherTimer's and is linking the Tick event of the timers with the RunMainSyncForTask() method, which inside, create a background worker that fetch the data from the server and save that data to a local database.
Now, this was taken from the MSDN
The DispatcherTimer is reevaluated at the top of every Dispatcher loop.
Timers are not guaranteed to execute exactly when the time interval occurs, but they are guaranteed to not execute before the time interval occurs. This is because DispatcherTimer operations are placed on the Dispatcher queue like other operations. When the DispatcherTimer operation executes is dependent on the other jobs in the queue and their priorities.
So, based on that I believe that the application is spamming threads every time a timer does a tick event, and this is done 7 times simultaneously; and all these operations, because of the DispatcherTimer nature, are being added to the Dispatcher queue, which makes the GUI response slow, due to the Dispatcher being busy.
Also, another problem with the application is that, when it runs, it takes about 90-95% of the CPU, I think that if my hypothesis is right, this could be also the cause of for this problem.
So if you can share some insides about this I'll appreciate it.
Thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您获得了 90-95% 的 CPU 使用率,因为您已经通过疯狂的线程调用网络建立了一种忙等待形式。
如果您使用此
StartSync
逻辑来发布状态通知或将数据返回到 GUI,那么您就会经历很多麻烦。如果您使用 .Net 4.0,则应切换到 任务并行库并让框架为您处理所有这些。它还支持优雅取消等。如果您不想使用 TPL,我建议改为传递
Window
的Dispatcher
(使用通常的嫌疑人:Invoke 或 BeginInvoke ) 或SynchronizationContext
(与Post
异步,与Send
同步)到各个任务,以用于必须在 GUI 中完成的这些任务。You're getting the 90-95% CPU because you've instituted a form of busy waiting through a crazy web of threading calls.
If you're using this
StartSync
logic to post status notifications or get data back to the GUI, you're jumping through a lot of hoops. If you're on .Net 4.0 you should switch to the Task Parallel Library and let the framework handle all of this for you. It also supports graceful cancellation, etc.If you don't wish to use TPL, I would suggest instead passing the
Window
sDispatcher
(use the usual suspects: Invoke or BeginInvoke) orSynchronizationContext
(asynchronously withPost
, synchronously withSend
) to the individual tasks for use for these tasks which must be done in the GUI.