简体   繁体   中英

Prevent hanging in windows forms

I'm having a problem. So I've built an app which displays data in the form of chart and a datagridview. They are both responsive. That means they rescale and move with the data. It takes some computation power I guess.

At the same time I have timers cause it all runs periodically with f=4Hz.

And now: When I run the app and switch on the periodical readout the app hangs during resizing. How could I prevent it?

I've already tried to use a backgroundworker, but the problem occurs in the moment of accessing to the datagridview and chart which are declared (and also used) in the "other thread" (as the VS said)

So.. How could I prevent it? Maybe I should utilise the backgroundworker in the other way?

My attempts with the backgroundworker:

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();
        }

Please take a look at background worker sample. You are doing it wrong. Background worker DoWork should not call UI controls and is executed in non UI thread, it should execute time consuming computing and call worker.ReportProgress(). While ReportProgress method can access UI controls and code in this method is executed in UI thread. Some chart controls are lugging when adding/removing points. Maybe it hangs because it lugs. Make updates less frequently (1 in 1 second for example) and see whether it hangs or not.

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

Wrap operations in Stopwatch and use System.Diagnostics.Debug.WriteLine to trace execution flow and time spent on the operations.

Moving chart part does not work because it accesses UI elements in non ui thread without Invoke to UI thread.

If it was not Background worker I would write it this way:

    //  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;
          }
     }
)); 

I wouldn't wrap each operation in separate Invokes as well.

As for your question it's insufficient information to detect what is wrong please provide minimum viable runnable sample which demonstrates the problem.

As @Access Denied states you should improve separation between GUI and Background worker threads. You could execute // TEMP READ and // TEMP IMPORT operations on background thread and make a call to the GUI thread via .Invoke method when all the data is ready. Read "How to: Make Thread-Safe Calls to Windows Forms Controls" article for more information.

When you add/update data in your DataGridView use .BeginUpdate / .EndUpdate methods to prevent control update until all the data is refreshed.

Other approach is to use Virtual mode . It's especially usefull if you have many items in grid.

When working with a background thread you must not create, update, or even access any UI element.

You need to separate the work that retrieves your data (the slow part) from the work that updates the chart (which is very fast).

It really comes down to doing it like this:

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();
    }));
}

If you have to show a MessageBox then you also need to invoke that.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM