使用C#表单实时绘制多个图表

Bri*_*ker 5 c# charts multithreading timer

我想从A / C的16个通道中获取预览,我需要25-40Hz的刷新率(每25-40ms更新一次)。我很少使用线程和计时器的组合,但仅在最多4个图表上就获得了令人满意的性能。在扩展图表范围以刷新后,添加的图表的帧速率约为0.5 / s。我应用了快速折线图。
我应用了一个计时器,每20毫秒从A / C获取新数据。经过一些测试,看起来添加一个单独的线程来处理每个图表(在给定时间内休眠,然后更新绘图)效率不高(至少按照我的方式)。
所以问题是:如何有效地处理多个图表。
下面,我介绍了代码的最重要部分。

    System.Timers.Timer tim = new System.Timers.Timer(20);
    System.Threading.Thread[] t;
    int HighChan = 15;
    //button which runs the preview, executed once
    private void previewB_Click(object sender, EventArgs e)
    {                                            
        t = new System.Threading.Thread[HighChan + 1];
        for (int i = 0; i <HighChan+1; i++)
        {                    
            charts[i].Series.SuspendUpdates();
            //run a separate thread for each chart
            t[i] = new System.Threading.Thread(new ParameterizedThreadStart(updatePlot));
            t[i].Start(i);                    
        }

        //run timer to get new data with tim.interval                    
        tim.Stop();               
        tim.Elapsed += new ElapsedEventHandler(this.OnTimedEvent);           
        tim.Start();                                
    }

    ushort[] ADData_prev;
    //get new data from A/C every tim.interval
    private void OnTimedEvent(object sender, EventArgs e)
    {
        ADData_prev = getPrev();  //gets new data, array wit 16 fields
        // I also tried to suspend and resume threads t from this place but unsuccessfully
    }


    //update the chart with new data
    void updatePlot(object chart_info)
    {          
        int i = (int)chart_info;

        while(true)
        {
                //wait for new data to read
                Thread.CurrentThread.Join(20);

                charts[i].Invoke(new Action(delegate()
                { charts[i].ResetAutoValues(); }));

                // I skipped some conditions to make code clearer                              
                //remove old point and add new one
                charts[i].Invoke(new Action(delegate()
                {
                    charts[i].Series[0].Points.RemoveAt(0);
                    charts[i].Series[0].Points.AddY(ADData_prev[i]);
                }));                     

                charts[i].Invoke(new Action(delegate()
                {
                    charts[i].Series.ResumeUpdates();
                    charts[i].Series.Invalidate();
                    charts[i].Series.SuspendUpdates();
                }));

          }
    }
Run Code Online (Sandbox Code Playgroud)

更新:1.
我已经将updatePlot()的功能移到了计时器的onTimedEvent上,所以现在看起来像这样:

    private void OnTimedEvent(object sender, EventArgs e)
    {
        ADData_prev = getPrev();  //gets new data, array wit 16 fields

        charts[0].Invoke(new Action(delegate()
        {

           for (int i = 0; i < HighChan + 1; i++)
           {
               //charts[i] stuff here
           }
        }
    }
Run Code Online (Sandbox Code Playgroud)

2.我决定在onTimedEvent中更改bool变量,该变量允许绘制一次图表,每个计时器的周期都在updatePlot()的while(true)循环中:

    private void previewB_Click(object sender, EventArgs e)
    {                                            
        for (int i = 0; i <= HighChan; charts[i++].Series.SuspendUpdates()) ;
        t = new System.Threading.Thread(updatePlot);
        t.Start();

        //run timer to get new data with tim.interval                    
        tim.Stop();               
        tim.Elapsed += new ElapsedEventHandler(this.OnTimedEvent);           
        tim.Start();                                
    }

    bool entry = false;
    private void OnTimedEvent(object sender, EventArgs e)
    {
        ADData_prev = getPrev();  //gets new data, array wit 16 fields
        entry = true;            
    }

    void updatePlot()
    {
       while(true)
       {
          if(entry)
          {
               charts[0].Invoke(new Action(delegate()
               {
                  for (int i = 0; i < HighChan + 1; i++)
                  {
                     //charts[i] stuff here
                  }
               }
          entry = false;
          }
       }
    }
Run Code Online (Sandbox Code Playgroud)

这些解决方案仅做了很小的改进。不幸的是,在两种情况下,图表的刷新速度都不够快。另外,Charts []数组中的前8个刷新速度很快,而后8个具有约0.5 Hz的帧速率,这对我来说很奇怪,因为它们的行为方式不同。


更新2:

如第一篇文章所述,我需要每20-40ms重绘一次图表。当我只绘制一个图时,我可以达到该帧速率,因此与数据收集时间无关(A / C在Fs = 1k Hz的背景下工作)。也许将队列绑定到图表可以使其速度更快,但幅度却不大。

我将更精确地描述我进行的性能测试中发生了什么。
因此,当我将nr(要刷新的图表数)限制为6时,以ti(时间间隔)20ms更新它们,因此它们一次存储点数(np)= 50,它们都运行平稳。但是,当我将np更改为100时,只有图表1-4可以平稳运行,而当5的速度非常慢而6的速度几乎停止时。当nr = 6,ti = 20,np = 250时,图表1-3运行平稳,而图表4-6运行速度为0.1fps。

当nr = 10,ti = 20,np = 50时,图表1-6的行为与在nr = 6情况下的行为相同,而图表7-8出人意料地运行平稳,而图表9-10则表现为1fps。当nr = 10,ti = 20,np = 100时,图表1-6的行为与nr = 6情况下的行为相同(因此,仅图表1-3运行平稳),而图表7-8仍然运行平稳,而图表9- 10就像0.1fps。

当nr = 16时,ti = 20,np = 50时,图表1-8的行为类似于nr = 10的情况,而其他8种行为则类似于0.05fps或类似的值。

无论我使用哪种方法,对于单线程,多线程,单次/多次Invoke,队列绑定,Thread.Sleep(),Threed.CurrentThread.Join(),Timer,异步,结果总是不一样。老实说,我不知道为什么无法在16条快速折线图上获得实时数据预览。
我将感谢您的任何建议。

Chr*_*ard 3

老实说,我不认为多线程在这里对你有任何帮助。归根结底,最繁重的操作将是更新图表,无论如何,这都必须在 UI 线程上完成。

我建议您使用一个线程,每 20 毫秒获取一次所有最新值,然后Invoke在 UI 上调用一个线程,该线程在单个Action. 每次更新调用多次会导致显着的性能损失。