0

我知道这是我之前问过的问题的转贴……

c#想要多个ui线程但得到交叉引用错误

...但是我的后续问题没有得到回答,所以我再次发布以寻求有关新问题的帮助。我会在这里重复介绍。谢谢你的放纵。

我对 c#、线程和表单还是很陌生。我写了一个小数据采集程序。它有两个线程:一个传感器轮询/记录线程和一个可以绘制传感器数据图表的主 UI 线程。当用户单击“开始记录”按钮时,它会不断轮询传感器(通过虚拟 COM 端口),将响应写入文件,使用一些基本轮询统计信息(每秒轮询次数)更新主窗体。如果用户单击了“监视器”按钮,它会打开一个图表表单,并且轮询线程开始调用一个将传感器值添加到图表的方法。该程序运行良好,但我发现如果我打开了多个图表(以便我可以实时查看多个传感器),图表更新会变得零星或停止,只有焦点窗口会顺利更新。

所以我有了制作图表线程的“聪明”想法,认为这将提供多个 UI 循环(因此我可以与每个图表进行交互)并且会在多个图表表单上生成漂亮的平滑图表。以下是简化代码;例如,在这里,图表线程由轮询线程启动,而不是在用户单击“监控”按钮时启动。

问题是委托从未被执行。主窗体上的统计信息正在很好地更新,图表窗体已显示,但没有响应,当我使用鼠标时出现“等待”光标。非常感谢您的建议。谢谢。

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

2 回答 2

1

问题出在您的第二个 UI 线程中。您不能在 UI 线程中放置无限循环并期望它能够工作:

    while (charting_is_active) { }

UI 线程需要运行 windows 消息队列。我的建议是您只在初始 UI 线程中创建这两个表单。但是,如果您仍然想使用两个线程的方法,我认为您应该执行以下操作:

private void charting_thread_start(object sender, DoWorkEventArgs e)
{
    Thread.CurrentThread.Name = "charting";
    Chart_Form chart_form = new Chart_Form();
    Application.Run(chart_form); 
}
于 2013-06-20T02:28:08.810 回答
0

要跟进您的 dotTrace 数据:仔细查看这些数字。138 次 OnPaint 调用超过约 8 秒(绘制图表需要 58 毫秒)。另请注意,您已调用 BeginInvoke 2630 次! update_logging_stats处理了 2000 多次 - 您的轮询线程似乎运行得太快了。它向 UI 线程提供工作的速度比您的眼睛所见或显示器甚至可以渲染的速度都要快。

由于您update_logging_stats每次更新图表时调用一次,这意味着您的 Windows 消息队列已经积累了大量的绘制消息积压并且无法全部跟上它们(这会导致您的 UI 线程阻塞)。你只是给它太多的工作要做(远远超过必要的)。当它忙于绘制图表时,又有二十条消息进来重新绘制它。最终它最终尝试为队列提供服务并锁定。

您可以尝试添加秒表并测量您对图表的需求 - 仅每 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")});
    }
}

当然,如果您确实需要这种分辨率的数据,您仍然可以保留所有数据 -sensor_values当您不将它们添加到图表时执行其他操作(将它们保存到数组、文件等)。您甚至可以考虑在 200 毫秒左右的时间跨度内收集多个数据点,然后一次发送一组点进行绘图(而不是尝试每秒重新绘制整个数据集一百次) - 如果您真的在积累那个速度的数据。

于 2013-06-20T23:36:25.087 回答