【问题标题】:Real Time chart performance issue c# winform实时图表性能问题 c# winform
【发布时间】:2020-05-28 07:05:09
【问题描述】:

我目前正在使用 liveChart 绘制 3 个值的实时图表:位置、负载和变形。该程序基于 Doli.DoPE 库(专有 dll)

MainForm.cs 中,每次传感器记录一个新值(每毫秒左右)都会触发一个事件。

public void Initialisation()
{
   //...    
   MyEdc.Eh.OnDataHdlr += new DoPE.OnDataHdlr(OnData)
   //...
}

private int OnData(ref DoPE.OnData Data, object Parameter)
{
    DoPE.Data Sample = Data.Data;
    if (Data.DoPError == DoPE.ERR.NOERROR)
    {
        Int32 Time = Environment.TickCount;
        if ((Time - LastTime) >= 250 /*ms*/)
        {
            // Send the data from the ondata handler inside of a global list
            ListData.time.Add(Sample.Time);
            ListData.position.Add(Sample.Sensor[(int)DoPE.SENSOR.SENSOR_S]);
            ListData.load.Add(Sample.Sensor[(int)DoPE.SENSOR.SENSOR_F]);
            ListData.extend.Add(Sample.Sensor[(int)DoPE.SENSOR.SENSOR_E]);

            Thread ThForUpdateChart = new Thread(() =>
            {
                if (NewINstanceOfChart != null)
                { NewINstanceOfChart.UpdateValues(ListData.time.Last(), ListData.position.Last(),ListData.load.Last(), ListData.extend.Last()); }
            });
            ThForUpdateChart.Start();
            LastTime = Time;
        }
    }
    return 0;
}

函数UpdateValues 是第二个表单RealTimeChart.cs 的一部分,通过按钮单击事件在MainForm 中调用:

private void btnGraph_Click(object sender, EventArgs e)
{
    var thread = new Thread(() =>
    {
        NewINstanceOfChart = new RealTimeChart(ListData);
        NewINstanceOfChart.Show();
    });
    thread.Start();
}

RealTimeCharts.cs 的形式是这样初始化的:

public RealTimeChart(Globals ListData)
{
    InitializeComponent();

    //measures = ListData;

    ListPosition = new ChartValues<ObservablePoint>();
    for (int i = 0; i < measures.load.Count(); i++)
    {
        ListPosition.Add(new ObservablePoint
        {
            X = measures.time[i],
            Y = measures.position[i]
        });
    }
    ListLoad = new ChartValues<ObservablePoint>();
    for (int i = 0; i < measures.load.Count(); i++)
    {
        ListLoad.Add(new ObservablePoint
        {
            X = measures.time[i],
            Y = measures.load[i]
        });
    }

    ListExtend = new ChartValues<ObservablePoint>();
    for (int i = 0; i < measures.load.Count(); i++)
    {
        ListExtend.Add(new ObservablePoint
        {
            X = measures.time[i],
            Y = measures.extend[i]
        });
    }

    resultChart.Series.Add(new LineSeries
    {
        LineSmoothness = 0,
        Values = ListPosition,
        PointGeometrySize = 2,
        StrokeThickness = 4
    });


    SetXAxisLimits();

}

UpdateValues函数定义如下:

        public void UpdateValues(double time, double position, double load, double extend)
        {
            measures.time.Add(time-measures.TareTime);
            measures.position.Add(position);
            measures.load.Add(load);
            measures.extend.Add(extend);

            UpdateEnabledSequencialPartToTrue();

        }



        public void UpdateEnabledSequencialPartToTrue()
        {
            if (this.InvokeRequired)
                BeginInvoke(new System.Action(() => this.InternalUpdateEnabledSequencialPartToTrue()));
            else
                InternalUpdateEnabledSequencialPartToTrue();
        }
        private void InternalUpdateEnabledSequencialPartToTrue()
        {
            try
            {
                ListPosition.Add(new ObservablePoint
                {
                    X = measures.time.Last(),
                    Y = measures.position.Last()
                });

                ListLoad.Add(new ObservablePoint
                {
                    X = measures.time.Last(),
                    Y = measures.load.Last()
                });

                ListExtend.Add(new ObservablePoint
                {
                    X = measures.time.Last(),
                    Y = measures.extend.Last()
                });

                //LineSeries plot = new LineSeries();
                SetXAxisLimits();

                // lets only use the last 14400 values (1h long recording, 14400 values at frequency of 1 record very 250ms, see OnData function MainForm
                if (measures.time.Count > 14400)
                {
                    ListPosition.RemoveAt(0);
                    ListLoad.RemoveAt(0);
                    ListExtend.RemoveAt(0);
                }
            }
            catch (NullReferenceException) { }
        }

一分钟后,程序开始变得非常滞后。我尝试将第二个 winform (RealTimeCharts) 放在另一个线程上,这样 MainForm 就不会滞后(它正在驾驶一台机器,它必须是响应式的),但没有成功。

我想知道整个事情是否因为代码太糟糕而滞后,或者是否是 liveChart 达到了它的(免费)限制。您能建议另一种绘制实时数据的方法吗?

【问题讨论】:

  • 在哪里更新值LastTime?还是您忽略了这样做?
  • @TnTinMn 很好,当我编辑代码时它丢失了。它在 250 毫秒循环结束时
  • 重新插入该行是否仍然存在问题?
  • @TnTinMn 是的,它一直在。这是我的错误副本/过去,抱歉;

标签: c# winforms performance real-time


【解决方案1】:

在 MainForm.cs 中,每次传感器记录一个新值(每毫秒左右)都会触发一个事件。

这自然比 Winforms 绘图可以采用的要高得多。看,绘制一个 GUI 是很昂贵的。如果您只为每个用户触发的事件执行一次,您将永远不会注意到这一点。但是从一个循环中进行 - 包括对每个 MS 采样一个传感器 - 你可以快速控制 UI。我的第一个多线程测试实际上似乎在大数字上失败了,因为我最终发送了这么多更新,我明显地超载了 GUI 线程。从那时起,我就知道不要越过进度条。

您可以尽可能快地将数据添加到背景集合中,但您无法快速绘制数据。老实说,绘制频率高于每秒 30-60 次(每 ~17 毫秒)无论如何都不会真正帮助任何人。通常你不能使用计时器,因为 Tick 可能会更频繁地发生然后它可以被处理 - 再次,一个带有溢出事件队列的 GUI 线程。

我没有任何用于 WindowsForms 的速率限制代码。但我猜想在完成工作后在 EventQueue 末尾重新排队的 Event 会起作用。

【讨论】:

  • 感谢您的快速回复!我完全同意你的观点,即不需要每毫秒绘制一次值。但是在 OnData 函数中,UpdateValues 函数每 250 毫秒触发一次,是不是太高了?只要我不启动图表,程序就可以运行几个小时而没有问题,但 MainForm 的 GUI 正在以相同的速度更新(没有把那个界面的代码放在那里,应该吗?)
  • @MaximeS 如果绘制需要251ms或更长的时间,确实很高。您将在 EventQueue 中开始累积更改事件。如果绘制需要 500 毫秒,则每秒将获得 2 个堆叠的 Draw Opeartions。并且需要同样长的时间来解决它们。 |并且绘图速度也可以是非静态的。有时可能需要更长的时间,因为您也在处理垃圾收集器。这就是为什么我更喜欢速率限制:我只画尽可能快的速度,但永远不会比 X 快。
  • 哦,好吧,我明白你的意思了。对我来说切换到 WPF 可能是值得的。我知道这并不容易,但我已经为此苦苦挣扎了太久。我已经阅读了很多不错的教程来使用 WPF 从头开始​​构建实时图表。我会测试,我会尝试使用速率限制功能来避免同样的问题。
  • WPF 在绘图时应该更快。然而,它是为一种称为 MVVM 的独特模式而设计的。只要它只是输出,它应该可以正常工作。速率限制可以通过不使用绑定而只添加显式轮询来完成。我写了一篇关于 MVVM 的介绍,它仍然可以帮助您入门:social.msdn.microsoft.com/Forums/vstudio/en-US/…
  • 那么我将从您的指南开始。不过,我不打算结束这个问题,因为这种重写代码的解决方案有点极端。我一直想尝试 WPF 有一段时间了,但是有类似问题的人,以及更大的代码可能不会认为这是一个好的选择。我认为等待一点是公平的。非常感谢您的投入和时间!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-09-20
  • 1970-01-01
  • 1970-01-01
  • 2010-12-04
  • 2011-04-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多