1.场景
使用以太网接收单片机发送的PHY电流的数据包,并在基于WPF实现的C#应用程序上进行分析、显示。上位机采用LiveCharts第三方控件用以显示数据(问题所在),电流采样频率为2ms,每采集20条电流发送一包数据。
2.代码实现
显示:通过向动态库的事件指针上添加回调函数,并在回调函数内将接收到的数据包存储至Queue,并在后台线程中循环取出队列中已有的数据包,并将其中的数据(电流、时间戳等)添加至已绑定LiveCharts对应的折线图序列,;
数据保存:队列中的数据包在添加至折线图时,也会存储至DataTable容器中,当容器中对象个数大于某个设定值后,将会开启新线程将表中数据写入.csv文件中。
分析:若包中数据满足某一条件(比如电流大于、小于某个值,时间戳大于某个值等等),调用正在等待的AutoResetEvent对象的Set()函数,使其所在线程继续执行(以满足其他测试需求),完成线程间的同步。
3.问题定位
这个设定值实际上会在两个地方使用。一是在数据保存前进行判断,二则作为LiveCharts绑定的数据源的一系列点个数的上限,产生导致界面卡死的原因在于,在数据保存的新线程中,移除了数据源溢出的点,错误部分的代码(下面代码在控件创建线程异步执行的Action中运行)如下:
this.Dispatcher.BeginInvoke((Action)(() =>
{
...//数据分析用的无关代码
Thread t = new Thread(new ThreadStart(delegate
{
//文件保存
SaveCSV(data_table, csv_path);
#region 操作数据源代码
//操作已绑定至LiveCharts控件的数据
while (Labels.Count > MaxPointsCount)
{
//移除X轴标签、Y轴两条折线数据源中起始的点
Labels.RemoveAt(0);
SeriesCollection[0]?.Values.RemoveAt(0);
SeriesCollection[1]?.Values.RemoveAt(0);
Thread.Sleep(1);
}
#endregion
}));
t.Start();
...//无关代码
}));
调试过程中,每次执行到这一步就会导致界面卡死(实际后台仍在接收并处理数据),对于多线程的理解不够,在将#region...#endregion中的内容从新建线程t的委托中移除并放至线程t外进行运行,问题解决。
最终认为,此问题和“不能在非控件创建线程中操作控件”是同一个道理,应该遵循WPF规则,在控件对象创建线程中对数据源进行操作。