简体   繁体   English

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

[英]C# SerialPort datareceived and WPF UI freezing

My goal is to spy a gsm module RX/TX communication on a custom system. 我的目标是在定制系统上监视gsm模块的RX / TX通信。 For this, I use 2 serial ports on my computer and a WPF c# app. 为此,我在计算机和WPF c#应用程序上使用2个串行端口。 On specific commands, the comunnication between the gsm module and the system is able to change for 9600bds, 57600bds or 125000bds. 通过特定命令,gsm模块与系统之间的通信可以更改为9600bds,57600bds或125000bds。 With 9600 and 57600, no problem. 使用9600和57600,没问题。 But when the com speed is 125000, my UI freeze. 但是当com速度为125000时,我的UI冻结了。 I have read a lot of posts about UI freezing with Action, Delegate, dispatcher but it doesn't work and I don't understand how to solve my issue. 我已经阅读了很多有关使用Action,Delegate,dispatcher冻结UI的文章,但是它不起作用,而且我不知道如何解决我的问题。

Here, what I'm doing for now: 在这里,我现在正在做什么:

  1. I have 2 serial ports from System.IO.Ports; 我有2个来自System.IO.Ports的串行端口;
  2. the 2 serial ports use the same SerialDataReceivedEventHandler 2个串行端口使用相同的SerialDataReceivedEventHandler
  3. inside the SerialDataReceivedEventHandler, I build a custom class instance of "QueueElement" with serialPort name, the bytes received and the hour when the event occurs. 在SerialDataReceivedEventHandler内,我用serialPort名称,接收的字节和事件发生的时间构建“ QueueElement”的自定义类实例。 This QueueElement is push in a Queue from 该QueueElement从以下位置推入队列
  4. every 100ms, a timer tick event read the Queue to display all item from it to the UI ListBox. 每隔100毫秒,计时器滴答事件就会读取Queue,以将队列中的所有项目显示到UI ListBox。

Here some code to explain how I proceed: 这里有一些代码来解释我如何进行:

// 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;   
}

And the xaml part for the ListBox: 还有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>

I use Visual Studio Community 2017. 我使用Visual Studio Community 2017。

I think you've got a race condition with the Queue. 我认为您的Queue有竞赛条件。 I assume QueueConsumerTimedEvent is running on the UI thread. 我假设QueueConsumerTimedEvent在UI线程上运行。 The while loop may be running forever because it is checking if queueList.Count > 0 but while processing the top item the DataReceived thread is adding more items to the queue. while循环可能永远运行,因为它正在检查queueList.Count> 0,但是在处理顶层项目时,DataReceived线程正在向队列添加更多项目。 So it never reaches zero and thus holds up the UI thread. 因此,它永远不会达到零,因此占用了UI线程。

You should at least move the Monitor.Enter to accomodate but then you run the risk of missing or delaying incoming data. 您至少应该移动Monitor.Enter以适应,但是这样会冒丢失或延迟传入数据的风险。 Probably should dequeue first so you can release the Monitor quickly then process the dequeued item. 可能应该先出队,以便您可以快速释放Monitor,然后处理出队的项目。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM