表单更新太昂贵,无法在Winform.Timer.Tick中执行

时间:2010-03-26 09:28:13

标签: c# multithreading

我有一个WinForm从可用数据中绘制图表。

我对它进行了编程,以便每{1}个事件调用一个函数:

  • 将出列所有可用数据
  • 将在图表上添加新点

现在要绘制的数据真的很大,需要花费大量时间才能更新我的表单。 Winform.Timer.Tick也依赖于Winform.Timer.Tick,因此它在表单的同一个线程中执行。

这两件事使我的形式非常反应迟钝。

我该怎么做才能解决这个问题?

我想到了以下内容:

  • 远离WM_TIMER的使用并开始使用Winform.Timer
  • 使用System.Threading.Timer模式,因此我将依赖.NET IsInvokeRequired

由于我有大量数据,这是个好主意吗? 我担心在某些时候ThreadPool也会太长或太大。

你能否就我的问题向我提出建议?

非常感谢!

AFG

3 个答案:

答案 0 :(得分:2)

将数据提取移动到线程是个好主意。您可以使用BackgroundWorker以无限循环的方式获取数据

  • 使用UpdateProgress事件更新图表。这将负责InvokeRequired业务
  • 在循环中使用Sleep(remainingTime)以获得所需的频率。

答案 1 :(得分:1)

我会在无限循环中使用System.Timers.Timer覆盖BackgroudWorker BackgroundWorker是从ThreadPool执行的,而并不意味着在应用程序的生命周期内运行。

System.Timers.Timer的动机:

  • 每个已发生的事件都是从ThreadPool执行的,不会挂起你的UI线程。
  • 使用锁定和启用/禁用计时器的组合,我们可以获得与在无限循环中执行Thread.Sleep(xxx)时相同的频率。
  • 更清洁,更明显的是你想要实现的目标

这是我的建议:
在方法开始时禁用计时器,然后在结束时再次重新启用计时器,将满足在已用事件中完成的工作量超过计时器间隔的情况。这还可确保更新之间的计时器保持一致。我已经添加了一个额外的预防措施锁。

我使用匿名方法来更新UI线程,但你可以随心所欲地做到这一点,只要你记得Invoke,检查InvokeRequired属性也是一个好主意。

private readonly object chartUpdatingLock = new object();

private void UpdateChartTimerElapsed(object sender, ElapsedEventArgs e)
{
    // Try and get a lock, this will cater for the case where two or more events fire
    // in quick succession.
    if (Monitor.TryEnter(chartUpdatingLock)
    {        
        this.updateChartTimer.Enabled = false;
        try
        {
            // Dequeuing and whatever other work here..

            // Invoke the UI thread to update the control
            this.myChartControl.Invoke(new MethodInvoker(delegate
                {
                    // Do you UI work here
                })); 
        }
        finally
        {
            this.updateChartTimer.Enabled = true;
            Monitor.Exit(chartUpdatingLock);
        }
    }
}

答案 2 :(得分:1)

使用后台计时器你不太可能领先。您的图表控件几乎肯定需要从创建的同一个线程更新。任何具有可见外观的控件都可以。这要求您在Elapsed事件处理程序中使用Control.BeginInvoke,以便更新代码在UI线程上运行。出列数据可能不是很昂贵,实际上你可以通过调用使它变慢。并且还没有从UI线程中消除压力。

你也会有一个潜在的严重限制问题,即使UI线程无法跟上,计时器也会继续滴答作响并提取数据。这最终会使你的程序崩溃。

相反,请考虑使更新图表的代码更智能。如果此类详细信息至少为像素宽,则图表只能显示数据的详细信息。实际上,它只能显示有效信息的2000像素。这并不多,更新2000个数据点不应该造成任何麻烦。