串行数据接收导致应用程序冻结。

3
我正在开发一个 Windows 窗体应用程序,通过按钮发送数据,并持续接收数据以更新一些仪表值。
我遇到的问题是,在读取串口时,如果数据传输速度太快,我的应用程序会响应非常慢,例如在读取时可能达到 1000Hz 的数据传输速率。
我附上了代码的简化版本。
是什么原因导致这个应用程序挂起?我了解到需要一些单独的线程来解决这个问题?但是如何实现呢?
namespace Motor
{
    public partial class Form1 : Form
    {
        SerialPort ComPort = new SerialPort();
        string InputData = String.Empty;
        delegate void SetTextCallback(string text);

        public Form1()
        {
            InitializeComponent();
            ComPort.DataReceived += 
                new System.IO.Ports.SerialDataReceivedEventHandler(port_DataReceived_1);
        }

        private void port_DataReceived_1(object sender, SerialDataReceivedEventArgs e)
        {
            InputData = ComPort.ReadExisting();
            if (InputData != String.Empty)
            {
                this.BeginInvoke(new SetTextCallback(SetText), new object[] { InputData });
            }
        }

        private void SetText(string text)
        {
            if (text.Contains("CM")) 
            {
                // process the text content here
            }
        }
    }
}

如果您使用后台工作器RunAsync e.Result = ComPort.ReadLine(),并在backgroundWorker1_RunWorkerCompleted事件处理程序中,您可以获取信息吗? - FaizanHussainRabbani
4个回答

2
这很可能是你的问题。
this.BeginInvoke(new SetTextCallback(SetText), new object[] { InputData });

如果你的串口正在抖动,那么它一直在尝试将数据回传到UI线程。你真的需要这么快地更新主UI线程吗?难道你不能每秒或定期地更新它吗?
关于该声明:
什么会导致应用程序挂起?我读到我需要一些单独的线程?但如何实现?
SerialPort.DataReceived事件
当从SerialPort对象接收到数据时,DataReceived事件将在辅助线程上引发。
也就是说,它已经在辅助线程上运行,因此启动另一个线程不会对您有所帮助。
更新
关于您的评论:
1000hz是硬件传输的数据速率,GUI没有必要那么快,但我希望指针移动平滑。
你仍然可以让它们平滑,但至少防止UI被限制是关键,尝试缓冲200-300毫秒左右。即只是玩弄它。

你好,1000hz是硬件传输数据的速率,GUI界面不需要那么快,但我希望仪表盘能够平滑移动。我会尝试您提供的解决方案,并在稍后回来更新。谢谢大家。 - user3759105

0

使用 ReadLine 来避免一个块的 Data Received

namespace Motor
{
     delegate void SetTextCallback(string text);
    public partial class Form1 : Form
    {
        SerialPort ComPort = new SerialPort();
        string InputData = String.Empty;
         SetTextCallback st;

        public Form1()
        {
            InitializeComponent();
            st = new SetTextCallback(SetText);

        }
    private void Form1_Load(object sender, EventArgs e)
    {
         OpenPort();
    }
        private void port_DataReceived_1(object sender, SerialDataReceivedEventArgs e)
        {
            InputData = ComPort.ReadLine();
            if (InputData != String.Empty)
            {
               this.Invoke(st,new object[] { InputData});
            }
        }

        private void SetText(string text)
        {
           Console.WriteLine(text);//To check the return value
            if (text.Contains("CM")) 
            {
                // process the text content here
            }
        }
    }
}

这个部分有很多返回值在InputData中;

返回值列表(请注意检查此值)

1. OK
2. >
3. + CMGS: ID_NUMBER_HERE
4. ERROR ERROR_NUMBER_HERE

还有其他的...

现在,当你想要放弃或继续使用这些值返回代码时,它取决于你的代码。

当然,在这段代码之前,你已经打开了PORT。因为如果PORT关闭了,这段代码就不再起作用。

打开端口指南

private void OpenPort()
{
      private System.IO.Ports.SerialPort ComPort;
       ComPort= new System.IO.Ports.SerialPort("COM1");//depends on your 
     Device COM

            ComPort.NewLine = System.Environment.NewLine;
            ComPort.BaudRate = 115200;
            ComPort.Parity = System.IO.Ports.Parity.None;
            ComPort.DataBits = 8;
            ComPort.StopBits = System.IO.Ports.StopBits.One;
            ComPort.Handshake = System.IO.Ports.Handshake.None;

            ComPort.ReadTimeout = 3600000;
            ComPort.WriteTimeout = 3600000;
            ComPort.Encoding = Encoding.GetEncoding("iso-8859-1");

            GC.SuppressFinalize(ComPort);

            SerialPortFixer.Execute("COM1");

            ComPort.DataReceived += new 
   System.IO.Ports.SerialDataReceivedEventHandler(port_DataReceived_1);

            ComPort.Open();

            GC.SuppressFinalize(ComPort.BaseStream);

            ComPort.DtrEnable = true;
            ComPort.RtsEnable = true;

   }

希望能有所帮助。


-1

我没有任何编写的示例代码,但我可以建议您使用线程后台工作器来读取数据,这非常容易理解。您可以观看简短的教程。尝试创建一个按钮,启动后台工作器,并使用该后台工作器读取数据而不会冻结。很抱歉我没有时间编写示例,但我认为这些是您应该关注的主题(后台工作器或线程)

希望这有所帮助。


-2

尝试实现异步等待

更多信息: https://learn.microsoft.com/en-us/dotnet/csharp/async

https://msdn.microsoft.com/en-us/library/hh156513(v=vs.120).aspx

这是一个快速的示例(虽然我无法验证它,因为我没有运行代码所需的必要条件,但应该可以给出一个想法):
namespace Motor
{
public partial class Form1 : Form
{
    SerialPort ComPort = new SerialPort();
    string InputData = String.Empty;
    delegate void SetTextCallback(string text);

    public Form1()
    {
        InitializeComponent();
        ComPort.DataReceived +=
            new System.IO.Ports.SerialDataReceivedEventHandler(port_DataReceived_1);
    }

    private async void port_DataReceived_1(object sender, SerialDataReceivedEventArgs e)
    {
        var size = ComPort.ReadBufferSize;
        var data = new byte[size];
        await ComPort.BaseStream.ReadAsync(data,0,size);
        InputData += ComPort.Encoding.GetString(data);
        SetText(InputData);
    }


    private void SetText(string text)
    {
        if (text.Contains("CM"))
        {
            // process the text content here

        }
    }


}
}

3
你能否提供一些示例代码来说明async/await如何帮助OP,而不是提及一个链接? - FaizanHussainRabbani
1
在我看来,再添加一个链接并不能改善你的评论。 - Uwe Keim
@FaizanRabbani 当然,我在处理中... 我在想也许有人比我先完成了... - zhack
@zhack 慢慢来,不是谁先回答的问题,而是谁能给出真正帮助OP的正确答案。 - FaizanHussainRabbani

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接