代码之家  ›  专栏  ›  技术社区  ›  TimothyP

如何改进这个性能不佳、糟糕的串行端口代码?

  •  3
  • TimothyP  · 技术社区  · 17 年前

    void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        Thread.Sleep(100);
        while (port.BytesToRead > 0)
        {
            var count = port.BytesToRead;
    
            byte[] buffer = new byte[count];
    
            var read = port.Read(buffer, 0, count);
    
            if (DataEncapsulator != null)
                buffer = DataEncapsulator.UnWrap(buffer);
    
    
           var response = dataCollector.Collect(buffer);
    
           if (response != null)
           {
               this.OnDataReceived(response);
           }
    
           Thread.Sleep(100);
        }    
    }
    

    如果我删除其中一个Thread.Sleep(100)调用,代码将停止工作。

    当然,这确实会减慢速度,如果大量数据流进入, 除非我睡得更香,否则它也会停止工作。 (在纯死锁中停止工作)

    请注意,数据封装器和数据收集器是组件 由MEF提供,但性能相当好。

    该类有一个Listen()方法,该方法启动后台工作程序 接收数据。

    public void Listen(IDataCollector dataCollector)
    {
        this.dataCollector = dataCollector;
        BackgroundWorker worker = new BackgroundWorker();
    
        worker.DoWork += new DoWorkEventHandler(worker_DoWork);
        worker.RunWorkerAsync();
    }
    
    void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        port = new SerialPort();
    
        //Event handlers
        port.ReceivedBytesThreshold = 15;
        port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
    
        ..... remainder of code ...
    

    更新: *只是简单介绍一下IDataCollector类的功能。 无法知道是否已发送所有字节的数据 在一次读取操作中读取。所以每次读取数据时,它都是 传递给DataCollector,当完成 检查同步字节、长度、crc和尾部字节。真正的工作 以后由其他类完成。 *

    我现在按照建议替换了代码,但仍然存在一些问题:

    void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
            var count = port.BytesToRead;
    
            byte[] buffer = new byte[count];
    
            var read = port.Read(buffer, 0, count);
    
            if (DataEncapsulator != null)
                buffer = DataEncapsulator.UnWrap(buffer);
    
            var response = dataCollector.Collect(buffer);
    
            if (response != null)
            {
                this.OnDataReceived(response);
            }     
    }
    

    您可以看到,这在快速稳定的连接下运行良好。 但并非每次接收数据时都调用OnDataReceived。 (有关更多信息,请参阅MSDN文档)。因此,如果数据变得支离破碎 在事件数据丢失时,只读取一次。

    现在我想起了为什么我一开始就有这个循环,因为 如果连接速度慢或不稳定,它实际上必须读取多次。

    显然,我不能回到while循环解决方案,那么我能做什么呢?

    3 回复  |  直到 17 年前
        1
  •  2
  •   JeffFerguson    17 年前

    我对原始的基于while的代码片段的第一个关注点是字节缓冲区的恒定内存分配。在这里放置一个“new”语句,专门用于.NET内存管理器,为缓冲区分配内存,同时获取在上一次迭代中分配的内存,并将其发送回未使用的池,以便最终进行垃圾收集。在一个相对紧密的循环中,这似乎需要做很多工作。

    我很好奇,如果在设计时以合理的大小(比如8K)创建此缓冲区,您将获得性能改进,这样您就不会有所有这些内存分配、释放和碎片。那会有帮助吗?

    private byte[] buffer = new byte[8192];
    
    void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        Thread.Sleep(100);
        while (port.BytesToRead > 0)
        {
            var count = port.BytesToRead;
    
            var read = port.Read(buffer, 0, count);
    
            // ... more code   
        }
    }
    

    • 循环迭代1:接收100字节;分配100字节的缓冲区
    • 循环迭代2:接收75字节;分配75字节的缓冲区

    在这种情况下,您实际上不需要重新分配缓冲区,因为在循环迭代1中分配的100字节缓冲区足以处理在循环迭代2中接收的75字节。无需销毁100字节的缓冲区并创建75字节的缓冲区。(当然,如果您只是静态地创建缓冲区并将其移出循环,这是没有意义的。)

        2
  •  1
  •   Henk Holterman    17 年前

    它可以是如此简单。。。

    要么使用DataReceived处理程序,但没有循环,当然也没有Sleep(),读取准备好的数据并将其推送到某个地方(队列或内存流),

    启动一个线程(BgWorker)并执行(阻塞)serialPort1.Read(…),然后再次推送或组装您获得的数据。

    编辑:

    Edit2,关于更新2:

    byte[] buffer = new byte[128];  // 128 = (average) size of a record
    while(port.IsOpen && ! worker.CancelationPending)
    {
       int count = port.Read(buffer, 0, 128);
       // proccess count bytes
    
    }
    

    现在,可能您的记录大小可变,您不想等待接下来的126个字节来完成一个。您可以通过减小缓冲区大小或设置读取超时来对此进行调整。要获得非常精细的粒度,可以使用port.ReadByte()。因为它是从ReadBuffer读取的,所以实际上并不慢。

        3
  •  0
  •   gary    15 年前

    private byte[] buffer = new byte[8192]; 
    var index = 0;
    void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {    
        index += port.Read(buffer, index, port.BytesToRead);
    } 
    
    void WriteDataToFile()
    {
        binaryWriter.Write(buffer, 0, index); 
        index = 0;
    }
    
    推荐文章