我正在尝试使用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
in我的主窗体线程,应用程序将永远不会返回,但速度要快得多。
这个测试已经完全错了,它无法证明什么。像DataGridView这样的控件基本上是线程不安全的,你不能从工作线程更新它们。 Winforms通常非常适合抛出IllegalOperationException,但只有在直接访问控件属性时才有效。不幸的是,它不会像你在这里使用那样抛出数据绑定。
这种bug很难诊断,因为它实际上并没有经常出错。你通常会得到一个绘画故障,控件没有显示你添加的行,你不会想到它。特别是当你以高速率添加它们时。但事故并不仅限于此,也可能触发死锁。您当然会注意到,您的UI只是冻结了。它只是不会发生,甚至一天或一周,从来没有你调试代码。这也使我们几乎无法找出你的程序失败的原因。
正确执行此操作需要以下两种方法之一:
对于串行端口,您可能会考虑第二个子弹。您必须使用Control.BeginInvoke()来确保在UI线程上进行更新。要使其具有高性能,您需要确保尽可能不频繁地调用UI线程。线程上下文切换非常慢,通常会徘徊在半毫秒左右。但在很大程度上取决于UI线程的繁忙程度。当你向它投掷行时,它通常很忙于尝试保持网格更新。
你拥有的一大优势是它只需要和人眼一样快。看到快速更新并不是很擅长,当你以超过每秒20次的速度运行时,它变得模糊不清。因此,更频繁地调用只是浪费了cpu周期。
因此,请务必在DataReceived事件处理程序中收集足够的行,以避免使用微更新来破坏UI线程。这几乎与批量更新一样昂贵。当生成行的速率大于UI线程可以添加它们并绘制它们的速率时,您可以从根本上解决它,尽管不太可能是串行端口。你会注意到,用户界面不再能够直观地绘制自己而不会响应鼠标和键盘。这种事故的唯一方法是故意降低工人的更新率。
并且不要忘记删除行,因为写入没有限制阻止数据表获得无限数量的行。那将是用OOM轰炸你的程序。需要一段时间,它会首先大幅减速,你可能会注意到UI首先是紧张性精神病。