【发布时间】:2018-12-24 22:24:25
【问题描述】:
我的目标是监视自定义系统上的 gsm 模块 RX/TX 通信。 为此,我在我的计算机上使用 2 个串行端口和一个 WPF c# 应用程序。 在特定命令下,gsm 模块与系统之间的通信可以更改为 9600bds、57600bds 或 125000bds。 用 9600 和 57600 没问题。 但是当 com 速度为 125000 时,我的 UI 冻结了。 我已经阅读了很多关于使用 Action、Delegate、调度程序冻结 UI 的帖子,但它不起作用,我不明白如何解决我的问题。
这里,我现在在做什么:
- 我有 2 个来自 System.IO.Ports 的串行端口;
- 2个串口使用相同的SerialDataReceivedEventHandler
- 在SerialDataReceivedEventHandler 中,我使用serialPort 名称、接收的字节数和事件发生的时间构建了一个“QueueElement”的自定义类实例。此 QueueElement 是从 推送到队列中的
- 每 100 毫秒,一个计时器滴答事件读取队列以将其中的所有项目显示到 UI 列表框。
这里有一些代码来解释我是如何进行的:
// queue to store data read on serial port before display
Queue<QueueElement> queueList;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
initPorts();
queueList = new Queue<QueueElement>();
QueueConsumerProcessRunning = false;
queueConsumer();
}
private void initPorts()
{
mySerialPort1.DataReceived += new SerialDataReceivedEventHandler(SerialPort_DataReceived);
mySerialPort2.DataReceived += new SerialDataReceivedEventHandler(SerialPort_DataReceived);
// ...
}
// data on serial port
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Monitor.Enter(queueList);
try
{
// create a queue element and add it to the queue
// ...
queueList.Enqueue(queueSingleElement);
}
finally
{
Monitor.Exit(queueList);
}
}
private void queueConsumer()
{
Thread backgroundThread = new Thread(
new ThreadStart(() =>
{
while (true) // always alive
{
if ((QueueConsumerProcessRunning == false) && (queueList.Count > 0))
{
Dispatcher.Invoke(() => { QueueCount.Text = queueList.Count.ToString(); });
QueueConsumerProcessRunning = true;
QueueElement Qelem;// = new QueueElement();
Monitor.Enter(queueList);
try
{
// get alodest element
Qelem = queueList.Peek();
// remove oldest element
queueList.Dequeue();
}
finally
{
Monitor.Exit(queueList);
}
// call method to display data
AddDataMethod(buildSerialElement(Qelem.SerialPortName, Qelem.ReadBytes, Qelem.EventHour));
QueueConsumerProcessRunning = false;
}
Thread.Sleep(100);
}
}
)); // backgroundThread
backgroundThread.IsBackground = true;
backgroundThread.Start();
}
// display received data
// when incoming data is from the same serial port of last received data: add data on last written row except if it's a carriage return
private void AddDataMethod(SerialElement elem)
{
if (!Dispatcher.CheckAccess()) // CheckAccess returns true if you're on the dispatcher thread this.ListBoxSpy.Disp...
{
Dispatcher.Invoke(new AddDataDelegate(AddDataMethod), elem);
return;
}
// update List<SerialElement>
// ...
// and call the function to update item displayed on UI
refreshDisplay();
}
// refresh display
private void refreshDisplay()
{
// create a list of ListBoxRowItem
// ...
// and display the list on ListBox UI
ListBoxSpy.ItemsSource = myListBoxRows;
}
ListBox 的 xaml 部分:
<ListBox Name="ListBoxSpy" HorizontalAlignment="Stretch" Margin="0,100,0,0" VerticalAlignment="Stretch" FontFamily="Courier New" >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Message}" Foreground="{Binding MessageColor}" VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.VirtualizationMode="Recycling" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
我使用 Visual Studio Community 2017。
【问题讨论】:
-
尝试使用任务在不同的线程中执行耗时的操作。
-
@Babbillumpa:根据你的建议,我尝试了
Thread t = new Thread(new threadStart(() => { /*consumer here*/ })); t.Start();和Task t = Task.Run(() => { /*consumer here*/}); t.Wait();,但我的 UI 再次冻结。 -
请不要发布所有您拥有的代码。为了帮助您解决问题,我们需要一个minimal reproducible example,强调minimal。
标签: c# wpf user-interface serial-port freeze