简体   繁体   English

C#无响应第二UI线程

[英]c# non-responsive second ui thread

i know this is a repost of a previous question i asked... 我知道这是我之前问过的问题的转贴...

c# wanting multiple ui threads but getting cross-reference errors instead C#要多个ui线程,但却得到交叉引用错误

...but my followup question wasn't answered so i'm posting again for help on the new problem. ...但是我的后续问题未得到解答,因此我再次发布有关新问题的帮助。 i'll repeat the intro here. 我在这里重复介绍。 thanks for you indulgence. 感谢您的放纵。

i'm still very new at c#, threads and forms. 我在C#,线程和表单方面还是很新的。 i've written a small data acquistion program. 我已经编写了一个小型数据采集程序。 it has two threads: a sensor polling/logging thread and a main UI thread which can chart the sensor data. 它有两个线程:一个传感器轮询/记录线程和一个可以绘制传感器数据图表的主UI线程。 when the user clicks the "start-logging" button, it continuously polls the sensors (over a virtual COM port), writes the response to a file, updates the main form with some basic polling stats (how many pollings per second). 当用户单击“开始记录”按钮时,它将连续轮询传感器(通过虚拟COM端口),将响应写入文件,并使用一些基本的轮询统计信息(每秒多少次轮询)更新主窗体。 if the user has clicked a "monitor" button, it opens a charting form and the polling thread begininvokes a method that adds the sensors values to the chart. 如果用户单击了“监视”按钮,则将打开一个图表表格,并且轮询线程将开始调用将传感器值添加到图表的方法。 the program works well but i found that if i have multiple charts open (so that i can view multiple sensors in realtime), the chart updates become sporadic or stop and only the window with the focus updates smoothly. 该程序运行良好,但我发现如果我打开了多个图表(以便可以实时查看多个传感器),则图表更新会变得零星或停止,只有具有焦点的窗口才能平滑更新。 (the comm port is only 56kbaud so it's not like the polling is being swamped with data.) (comm端口只有56kbaud,所以这不像轮询中充满了数据。)

so i got the "bright" idea to make charting threads, thinking this would provide multiple UI loops (so i could interact with each chart) and would produce nice smooth charting on multiple chart forms. 因此,我想到了制作图表线程的“聪明”主意,认为这将提供多个UI循环(以便我可以与每个图表进行交互),并且可以在多种图表形式上生成流畅的图表。 below is simplified code; 下面是简化的代码; eg here, the charting thread is started with the polling thread instead of when the user clicks the "monitor" button. 例如,在这里,制图线程从轮询线程开始,而不是在用户单击“监视器”按钮时启动。

the problem is that the delegate is never performed. 问题是永远不会执行委托。 the stats on the main form is being updated nicely, the charting form is displayed, but is unresponsive and i get the "wait" cursor when i mouse it. 主窗体上的统计信息正在很好地更新,显示了图表窗体,但是没有响应,当我用鼠标单击时,我得到了“等待”光标。 advice greatly appreciated. 建议非常感谢。 thanks. 谢谢。

namespace WindowsFormsApplication2
{
    public partial class Main_Form : Form
    {
        delegate void UpdateUIStatsDelegate(string update);
        UpdateUIStatsDelegate update_stats_delegate;

        static BackgroundWorker polling_thread = new BackgroundWorker();
        static BackgroundWorker charting_thread = new BackgroundWorker();

        public static Chart_Form chart_form;

        public Main_Form()
        {
            Thread.CurrentThread.Name = "main";

            update_stats_delegate = new UpdateUIStatsDelegate(update_stats);

            polling_thread.DoWork += polling_thread_DoWork;
            charting_thread.DoWork += charting_thread_start;
        }

        private void start_polling_Click(object sender, EventArgs e)
        {
            // start polling thread
            polling_thread.RunWorkerAsync();

            // start charting plotting thread
            charting_thread.RunWorkerAsync();
        }

        private void polling_thread_DoWork(object sender, DoWorkEventArgs e)
        {
            string sensor_values;
            Thread.CurrentThread.Name = "polling";

           while (true)
            {
                sensor_values = poll_the_sensors_and_collect_the_responses();
                chart_form.BeginInvoke(chart_form.update_chart_delegate, new object[] { sensor_values });

                pps = compute_polling_performance();
                BeginInvoke(update_stats_delegate, new object[] { pps.ToString("00") });
            }
        }

        private string poll_the_sensors_and_collect_the_responses()
        {
            send_command_to_sensor(sensor_id, command_to_return_current_readings);
            return read_sensor_response(sensor_id);
        }

        private void update_stats(string stat)
        {
            pollings_per_second.Text = stat;
        }

        private void charting_thread_start(object sender, DoWorkEventArgs e)
        {
            Thread.CurrentThread.Name = "charting";
            chart_form = new Chart_Form();
            chart_form.Show();
            while (charting_is_active) { }
        }
    }

    public partial class Chart_Form : Form
    {
        public delegate void UpdateChartDelegate(string sensor_values);
        public UpdateChartDelegate update_chart_delegate;

        public Chart_Form()
        {
            update_chart_delegate = new UpdateChartDelegate(update_chart);
            this.Text = "a realtime plot of sensor values";
        }

        private void update_chart(string sensor_values)
        {
            int x = extract_x_value(sensor_values);
            int y = extract_y_value(sensor_values);

            chart1.Series[X_AXIS].Points.AddY(x);
            chart1.Series[Y_AXIS].Points.AddY(y);
        }
    }
}

The problem is in your second UI thread. 问题出在您的第二个UI线程中。 You can not put a infinite loop in a UI thread an expect it to work: 您不能在UI线程中放入无限循环,否则它会起作用:

    while (charting_is_active) { }

The UI thread needs to run the windows message queue. UI线程需要运行Windows消息队列。 My advice is that you create both forms only in the initial UI thread. 我的建议是,您只能在初始UI线程中创建两个表单。 But if you still want to go with the two threads approach, I think you should do something like: 但是,如果您仍然想使用两个线程的方法,我认为您应该执行以下操作:

private void charting_thread_start(object sender, DoWorkEventArgs e)
{
    Thread.CurrentThread.Name = "charting";
    Chart_Form chart_form = new Chart_Form();
    Application.Run(chart_form); 
}

To follow up on your dotTrace data : take a close look at those numbers. 要跟踪您的dotTrace数据:请仔细查看这些数字。 138 calls to OnPaint over ~8 seconds (58ms to draw the chart). 约8秒内138次调用OnPaint(绘制图表的时间为58毫秒)。 Also note that you've called BeginInvoke 2630 times! 另请注意,您已致电BeginInvoke 2630次! update_logging_stats was handled over 2000 times - your polling thread seems to be running way too fast. update_logging_stats已处理2000次以上-您的轮询线程似乎运行得太快。 It's feeding work to the UI thread faster than your eyes can see or the display can even render. 它向UI线程提供工作的速度超过了您的眼睛甚至显示屏甚至可以呈现的速度。

Since you call update_logging_stats once for every time you've updated the chart, this means that your Windows message queue has accumulated an enormous backlog of paint messages and cannot keep up with them all (this is causing your UI thread to choke). 由于您每次更新图表后都调用一次update_logging_stats ,因此这意味着Windows消息队列已经积压了大量的绘制消息,无法跟上所有消息(这导致UI线程阻塞)。 You're simply giving it too much work to do (way more than is necessary). 您只是在给它太多的工作要做(远远超出了必要)。 While it is busy drawing the chart, twenty more messages have come in to paint it again. 在忙于绘制图表时,又有二十条消息再次绘制。 Eventually it ends up trying to service the queue and locks up. 最终,它最终尝试服务队列并锁定。

What you may try is something like adding a stopwatch and metering your demands on the chart - only send it an update every 200ms or so : 您可以尝试执行的操作,例如添加秒表并计量您对图表的需求-仅每200毫秒左右发送一次更新:

private void polling_thread_DoWork(object sender, DoWorkEventArgs e)
{
    string sensor_values;
    Thread.CurrentThread.Name = "polling";

    Stopwatch spw = new Stopwatch();
    spw.Restart();

    while (true)
    {
        sensor_values = poll_the_sensors_and_collect_the_responses();
        if (spw.ElapsedMilliseconds > 200)
        {
            chart_form.BeginInvoke(chart_form.update_chart_delegate,
                                                 new object[] { sensor_values });
            spw.Restart();
        }

        pps = compute_polling_performance();
        BeginInvoke(update_stats_delegate, new object[] {pps.ToString("00")});
    }
}

You can still keep all of the data, of course, if you really need it with such resolution - do something else with sensor_values when you are not adding them to the chart (save them to an array, file, etc). 当然,如果确实需要这样的分辨率,您仍然可以保留所有数据-在不将它们添加到图表中时(将它们保存到数组,文件等),对sensor_values进行其他操作。 You might even consider collecting a number of data points over a span of 200ms or so and then sending a cluster of points to plot at once (rather than trying to replot the whole set a hundred times per second) - again if you are really accumulating data at that speed. 您甚至可以考虑在200毫秒左右的时间范围内收集多个数据点,然后一次发送一组点以进行绘图(而不是尝试每秒重新绘制整个集合一百次)-如果您确实在积累数据以这种速度。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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