端口读取中的线程速度

时间:2013-11-30 12:10:30

标签: c# multithreading performance

我正在尝试使用c#创建一个Windows窗体应用程序,该应用程序读取串行端口并将值返回到DataTable。我创建了一个新线程(我试图创建一个同时包含线程和BackgroundWorker的线程)来读取端口,然后在DataTable中显示值。

我的问题是,即使没有端口读取,我也不能每秒获得超过100个值。这是我的代码没有端口读取(只是将相同的值写入DataTable的每一行:

namespace BackgroundWorkerExample
{
    public partial class BackgroundWorkerExample : Form
    {
        private int counter = 0;
        private bool threadRunning = false;
        private DataTable dt = new DataTable();

        public BackgroundWorkerExample()
        {
            InitializeComponent();
        }


        private void BackgroundWorkerExample_Load(object sender, EventArgs e)
        {
            //Clear DataTable
            dt.Clear();

            //Add Collumns
            dt.Columns.Add("Name");
            dt.Columns.Add("Value");
            dt.Columns.Add("Time", typeof(TimeSpan));


            dataGridView1.DataSource = dt;

        }

        private void StartButton_Click(object sender, EventArgs e)
        {

            threadRunning = true;
            Thread oThread = new Thread(countUp);
            oThread.Start();


        }

        private void StopButton_Click(object sender, EventArgs e)
        {
            //TimerLabel.Text = timer.ToString();
            threadRunning = false;
        }


        private void countUp()
        {
            while (threadRunning)
            {
                DataRow newRow = dt.NewRow();
                newRow["Name"] = counter;
                newRow["Value"] = 5;
                newRow["Time"] = DateTime.Now.TimeOfDay;
                dt.Rows.Add(newRow);

                counter++;

            }

        }


    }
}

这里方法countUp在一个单独的线程中运行。如果我没有创建新线程并在我的主表单线程中运行countUp,那么应用程序将永远不会返回,但速度要快得多。

1 个答案:

答案 0 :(得分:4)

这个测试已经完全错了,它无法证明什么。像DataGridView这样的控件基本上是线程不安全的,你不能从工作线程更新它们。 Winforms通常非常适合抛出IllegalOperationException,但只有在直接访问控件属性时才有效。不幸的是,它不会像你在这里使用那样抛出数据绑定。

这种错误很难诊断,因为它实际上并没有经常出错。你通常会得到一个绘画故障,控件没有显示你添加的行,你不会想到它。特别是当你以高速率添加它们时。但事故并不仅限于此,也可能触发死锁。你当然注意到,你的用户界面只是冻结了。它只是不会发生,甚至一天或一周,从来没有你调试代码。这也使我们几乎无法找到为什么你的程序失败。

正确执行此操作需要以下两种方法之一:

  • 通过在UI线程上将其DataSource属性设置为null 来取消绑定网格。然后在工作线程上创建数据表。然后通过在UI线程上设置DataSource 来完成工作程序后重新绑定网格。

  • 在worker上创建一个或多个DataRows,将它们添加到UI线程上的DataTable

对于串口,您可能会考虑第二个子弹。您必须使用Control.BeginInvoke()来确保在UI线程上进行更新。要使其具有高性能,您需要确保尽可能不频繁地调用UI线程。线程上下文切换非常慢,通常会徘徊在半毫秒左右。但在很大程度上取决于UI线程的繁忙程度。当你向它投掷行时,它通常很忙于尝试保持网格更新。

您拥有的一大优势是它只需要与人眼一样快。看到快速更新并不是很擅长,当你以超过每秒20次的速度运行时,它变得模糊不清。因此,更频繁地调用只是浪费了cpu周期。

因此,请确保在DataReceived事件处理程序中收集足够的行,以避免使用微更新来破坏UI线程。这几乎与批量更新一样昂贵。当生成行的速率大于UI线程可以添加它们并绘制它们的速率时,您可以从根本上解决它,尽管不太可能是串行端口。你会注意到,用户界面不再能够直观地绘制自己而不会响应鼠标和键盘。这种事故的唯一方法是故意降低工人的更新率。

并且不要忘记删除行,因为写入没有限制阻止数据表获得无限数量的行。那将是用OOM轰炸你的程序。需要一段时间,它会首先大幅减速,你可能会注意到UI首先是紧张性的。