【问题标题】:Thread speed in port reading端口读取中的线程速度
【发布时间】:2013-11-30 12:10:29
【问题描述】:

我正在尝试使用 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,应用程序将永远不会返回,但它会快得多。

【问题讨论】:

  • 今后,请在包含/关于代码的问题中添加诸如 c# 之类的编程语言标签。这将帮助熟悉相关编程语言的人找到您的问题。

标签: c# multithreading performance


【解决方案1】:

这个测试已经大错特错了,它不能证明任何事情。像 DataGridView 这样的控件基本上是线程不安全的,您不能从工作线程更新它们。 Winforms 通常可以很好地抛出 IllegalOperationException ,但这仅在您直接访问控件属性时才有效。不幸的是,它不会像您在此处使用的那样进行数据绑定。

这种错误很难诊断,因为它实际上并不会经常出错。您通常会遇到绘画故障,控件不显示您添加的行,而您对此一无所知。特别是当您以高速率添加它们时。但事故不仅限于此,触发死锁也是可能的。您肯定确实注意到,您的 UI 只是冻结了。它发生的频率还不够,可能一天或一周一次,永远不会在您调试代码时发生。这也使得几乎不可能找出您的程序失败的原因

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

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

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

对于串行端口,您可能会考虑第二个项目符号。您必须使用 Control.BeginInvoke() 来确保更新发生在 UI 线程上。为了使其高效,您需要确保尽可能不频繁地调用 UI 线程。线程上下文切换非常慢,通常徘徊在半毫秒左右。但很大程度上取决于 UI 线程的繁忙程度。当你向它扔行时,它通常很忙试图保持网格更新。

您拥有的一大优势是它只需要与人眼一样快。这不是很擅长查看快速更新,当您以每秒 20 次以上的速度进行更新时,一切都会变得模糊。因此,比这更频繁地调用只是浪费 cpu 周期。

因此,请务必在您的 DataReceived 事件处理程序中收集足够多的行,以避免微更新影响 UI 线程。这几乎与批量更新一样昂贵。当您生成行的速率大于 UI 线程可以添加和绘制它们的速率时,您可以让它从根本上崩溃,尽管对于串行端口不太可能。您会注意到 UI 不再明显地绘制自身并且不再响应鼠标和键盘。解决该事故的唯一方法是故意降低工作人员的更新率。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-09
    相关资源
    最近更新 更多