收到C#SerialPort数据和WPF UI冻结

时间:2018-07-17 09:19:11

标签: c# wpf user-interface serial-port freeze

我的目标是在定制系统上监视gsm模块的RX / TX通信。 为此,我在计算机和WPF c#应用程序上使用2个串行端口。 通过特定命令,gsm模块与系统之间的通信可以更改为9600bds,57600bds或125000bds。 使用9600和57600,没问题。 但是当com速度为125000时,我的UI冻结了。 我已经阅读了很多有关用Action,Delegate,dispatcher冻结UI的帖子,但是它不起作用,而且我不知道如何解决问题。

在这里,我现在正在做什么:

  1. 我有2个来自System.IO.Ports的串行端口;
  2. 两个串行端口使用相同的SerialDataReceivedEventHandler
  3. 在SerialDataReceivedEventHandler内,我用序列端口名称,接收的字节和事件发生的时间构建“ QueueElement”的自定义类实例。该QueueElement是从
  4. 推送到队列中的
  5. 每100毫秒,一个计时器滴答事件会读取Queue,以将队列中的所有项目显示到UI ListBox。

这里有一些代码来说明我如何进行:

// 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。

1 个答案:

答案 0 :(得分:0)

我认为您的Queue有竞赛条件。我假设QueueConsumerTimedEvent在UI线程上运行。 while循环可能永远运行,因为它正在检查queueList.Count> 0,但是在处理顶层项目时,DataReceived线程正在向队列添加更多项目。因此它永远不会达到零,因此会阻塞UI线程。

您至少应该移动显示器。输入以适应,但这样会冒丢失或延迟传入数据的风险。可能应该先出队,以便您可以快速释放Monitor,然后处理出队的项目。