防止挂在Windows窗体中

时间:2018-08-23 10:03:15

标签: c# multithreading windows-forms-designer

我有问题。 因此,我构建了一个应用程序,该应用程序以图表和datagridview的形式显示数据。他们都反应灵敏。这意味着它们将重新缩放并随数据一起移动。我猜这需要一些计算能力。

同时我有定时器,导致所有定时器都以f = 4Hz周期性运行。

现在:当我运行该应用程序并打开定期读数时,该应用程序在调整大小时会挂起。我该如何预防?

我已经尝试使用backgroundworker,但是在访问在“其他线程”(如VS所说)中声明(也使用)的datagridview和chart时,就会出现问题。

所以。如何预防? 也许我应该以其他方式利用背景工作者?

我与后台工作人员的尝试

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            //Control.CheckForIllegalCrossThreadCalls = false;
            if (!GetConnectionStatus())
            {
                stop_ticking();
                if (MessageBox.Show("Device not connected", "Connection status", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error) == DialogResult.Retry)
                    messaging();
                else
                    return;
            }

            //  TEMP READ
            Read_temp(tlist);
            float[] t = new float[3];
            float[] r = new float[3];
            float[] av = new float[1];
            float[] st = new float[1];

            //  TEMP IMPORT
            tlist.Give_current_temp(t, r, av, st);
            string time_stamp = tlist.Give_current_time();

            rows_nr++;

            //  ADDING TO GRID
            dataGridView1.Invoke(new Action(() => { dataGridView1.Rows.Add(new object[] { rows_nr, time_stamp, av[0], st[0], (t[0]).ToString(), (r[0]).ToString(), (t[1]).ToString(), (r[1]).ToString(), (t[2]).ToString(), (r[2]).ToString() }); }));
            //dataGridView1.Rows.Add(new object[] { rows_nr, time_stamp, av[0], st[0], (t[0]).ToString(), (r[0]).ToString(), (t[1]).ToString(), (r[1]).ToString(), (t[2]).ToString(), (r[2]).ToString() });
            dataGridView1.Invoke(new Action(() => { dataGridView1.FirstDisplayedScrollingRowIndex = dataGridView1.RowCount - 1; }));
            //dataGridView1.FirstDisplayedScrollingRowIndex = dataGridView1.RowCount - 1;

            //  ADDING TO CHART
            for (int i = 0; i < 3; i++)
                chart1.Invoke(new Action(() => { chart1.Series[series_names[i]].Points.AddXY((rows_nr), (t[i])); }));
            //chart1.Series[series_names[i]].Points.AddXY((rows_nr), (t[i]));
            chart1.Invoke(new Action(() => { chart1.Series["average"].Points.AddXY((rows_nr), (av[0])); }));
            //chart1.Series["average"].Points.AddXY((rows_nr), (av[0]));
            //chart1.Series["std1"].Points.AddXY((rows_nr), (av[0] + Math.Abs(st[0])));
            //chart1.Series["std2"].Points.AddXY((rows_nr), (av[0] - Math.Abs(st[0])));

            //  MOVING CHART
            if (chart1.Series[series_names[0]].Points.Count > nr_of_noints_graph)
            {
                for (int i = 0; i < 3; i++)
                    chart1.Series[series_names[i]].Points.RemoveAt(0);
                chart1.Series["average"].Points.RemoveAt(0);
                //chart1.Series["std1"].Points.RemoveAt(0);
                //chart1.Series["std2"].Points.RemoveAt(0);

                chart1.ChartAreas[0].AxisX.Minimum = rows_nr - (nr_of_noints_graph - 1);
                chart1.ChartAreas[0].AxisX.Maximum = rows_nr;
                dataGridView1.Rows.RemoveAt(0);
            }

            chart1.Invoke(new Action(() => { chart1.ChartAreas[0].RecalculateAxesScale(); }));
            //chart1.ChartAreas[0].RecalculateAxesScale();
        }

3 个答案:

答案 0 :(得分:1)

请查看后台工作人员样本。你这样做是不对的。后台工作程序DoWork不应调用UI控件,并且应在非UI线程中执行,它应执行耗时的计算并调用worker.ReportProgress()。虽然ReportProgress方法可以访问UI控件,但是此方法中的代码在UI线程中执行。 在添加/删除点时,某些图表控件比较笨拙。可能因为挂住而挂起。减少更新的频率(例如1秒1秒钟),然后查看其是否挂起。

https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.backgroundworker?view=netframework-4.7.2

在秒表中包装操作,并使用System.Diagnostics.Debug.WriteLine跟踪执行流程和操作时间。

移动图表部件不起作用,因为它在没有调用UI线程的情况下访问了非ui线程中的UI元素。

如果不是背景工作者,我会这样写:

    //  MOVING CHART
        chart1.Invoke(new Action(()=>
    {
          if (chart1.Series[series_names[0]].Points.Count > nr_of_noints_graph)
          {
               for (int i = 0; i < 3; i++)
                   chart1.Series[series_names[i]].Points.RemoveAt(0);
               chart1.Series["average"].Points.RemoveAt(0);
               chart1.ChartAreas[0].AxisX.Minimum = rows_nr - (nr_of_noints_graph - 1);
               chart1.ChartAreas[0].AxisX.Maximum = rows_nr;
          }
     }
)); 

我也不会将每个操作包装在单独的调用中。

对于您的问题,没有足够的信息来检测出问题所在,请提供最小的可运行样本以证明问题所在。

答案 1 :(得分:0)

@Access Denied 所述,您应该改善GUI和后台工作线程之间的分离。您可以在后台线程上执行// TEMP READ// TEMP IMPORT操作,并在所有数据准备就绪后通过.Invoke方法对GUI线程进行调用。阅读"How to: Make Thread-Safe Calls to Windows Forms Controls"文章以了解更多信息。

DataGridView中添加/更新数据时,请使用.BeginUpdate / .EndUpdate方法来防止控件更新,直到刷新所有数据。

其他方法是使用Virtual mode。如果网格中有很多项目,这将特别有用。

答案 2 :(得分:0)

在使用后台线程时,您不得创建,更新甚至访问任何UI元素。

您需要将检索数据的工作(较慢的部分)与更新图表的工作(非常快)分开。

这实际上归结为这样:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    if (!GetConnectionStatus())
    {
        stop_ticking();
        return;
    }

    //  TEMP READ
    Read_temp(tlist);
    float[] t = new float[3];
    float[] r = new float[3];
    float[] av = new float[1];
    float[] st = new float[1];

    //  TEMP IMPORT
    tlist.Give_current_temp(t, r, av, st);
    string time_stamp = tlist.Give_current_time();

    rows_nr++;

    chart1.Invoke(new Action(() =>
    {
        //  ADDING TO GRID
        dataGridView1.Rows.Add(new object[] { rows_nr, time_stamp, av[0], st[0], (t[0]).ToString(), (r[0]).ToString(), (t[1]).ToString(), (r[1]).ToString(), (t[2]).ToString(), (r[2]).ToString() });
        dataGridView1.FirstDisplayedScrollingRowIndex = dataGridView1.RowCount - 1;

        //  ADDING TO CHART
        for (int i = 0; i < 3; i++)
        {
            chart1.Series[series_names[i]].Points.AddXY((rows_nr), (t[i]));
        }

        chart1.Series["average"].Points.AddXY((rows_nr), (av[0]));

        //  MOVING CHART
        if (chart1.Series[series_names[0]].Points.Count > nr_of_noints_graph)
        {
            for (int i = 0; i < 3; i++)
            {
                chart1.Series[series_names[i]].Points.RemoveAt(0);
            }
            chart1.Series["average"].Points.RemoveAt(0);

            chart1.ChartAreas[0].AxisX.Minimum = rows_nr - (nr_of_noints_graph - 1);
            chart1.ChartAreas[0].AxisX.Maximum = rows_nr;
            dataGridView1.Rows.RemoveAt(0);
        }

        chart1.ChartAreas[0].RecalculateAxesScale();
    }));
}

如果必须显示一个MessageBox,则还需要调用它。