System.IO.Ports.SerialPort和多线程

Tim*_*hyP 3 .net c# performance serial-port

我有一些常常需要从串行接口(例如COM1)读取数据的SerialPort代码.但这似乎是CPU密集型的,如果用户移动窗口或者正在向窗口显示大量数据(例如通过串行线路接收的字节),那么通信就会搞砸.

考虑以下代码:

void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{

byte[] buffer = new byte[port.ReadBufferSize];

var count = 0;

try
{
    count = port.Read(buffer, 0, buffer.Length);
}
catch (Exception ex)
{
    Console.Write(ex.ToString());
}

if (count == 0)
    return;

//Pass the data to the IDataCollector, if response != null an entire frame has been received


var response = collector.Collect(buffer.GetSubByteArray(0, count));

if (response != null)
{
    this.OnDataReceived(response);
}
Run Code Online (Sandbox Code Playgroud)

需要收集代码,因为数据流是恒定的,并且必须分析数据(帧/分组).

    port = new SerialPort();

    //Port configuration code here...

    this.collector = dataCollector;

    //Event handlers
    port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
    port.Open();
Run Code Online (Sandbox Code Playgroud)

如果没有用户交互并且没有任何东西被添加到窗口,这可以正常工作但是只要有交互通信就会搞砸了.超时发生等......

例如,这会搞砸一切:

Dispatcher.BeginInvoke(new Action(() =>
{
  var builder = new StringBuilder();
  foreach (var r in data)
  {
      builder.AppendFormat("0x{0:X} ", r);
  }


  builder.Append("\n\n");

  txtHexDump.AppendText(builder.ToString());

  txtHexDump.ScrollToEnd();


}),System.Windows.Threading.DispatcherPriority.ContextIdle);
});
Run Code Online (Sandbox Code Playgroud)

但即使是对log4net的简单调用也会导致问题.

是否有任何最佳实践来优化SerialPort通信,或者有人能告诉我我做错了什么...

更新:

如果上述情况没有多大意义.我做了一个非常简单(和愚蠢)的小例子:

class Program
{
    static void Main(string[] args)
    {
        var server = new BackgroundWorker();
        server.DoWork += new DoWorkEventHandler(server_DoWork);
        server.RunWorkerAsync();

        var port = new SerialPort();
        port.PortName = "COM2";
        port.Open();
        string input = "";

        Console.WriteLine("Client on COM2: {0}", Thread.CurrentThread.ManagedThreadId);
        while (input != "/quit")
        {
            input = Console.ReadLine();
            if (input != "/quit")
            {
                var data = ASCIIEncoding.ASCII.GetBytes(input);
                port.Write(data, 0, data.Length);
            }
        }

        port.Close();
        port.Dispose();
    }

    static void server_DoWork(object sender, DoWorkEventArgs e)
    {
        Console.WriteLine("Listening on COM1: {0}", Thread.CurrentThread.ManagedThreadId);
        var port = new SerialPort();
        port.PortName = "COM1";
        port.Open();

        port.ReceivedBytesThreshold = 15;
        port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
    }

    static void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        var port = (SerialPort)sender;
        int count = 0;
        byte[] buffer = new byte[port.ReadBufferSize];
        count = ((SerialPort)sender).Read(buffer, 0, buffer.Length);

        string echo = ASCIIEncoding.ASCII.GetString(buffer,0,count);
        Console.WriteLine("-->{1} {0}", echo, Thread.CurrentThread.ManagedThreadId);
    }
}
Run Code Online (Sandbox Code Playgroud)

结果可能如下所示:

在COM2上监听COM1:6客户端:10这是我发送的一些示例数据---> 6这是我发送的一些示例数据

因此,从端口读取数据发生在主线程上....

这可能是造成我问题的部分原因吗?

小智 14

我很惊讶没有人抓到这个.使用DataReceived事件时,SerialPort类使用自己的线程.这意味着,例如,如果订阅者正在访问任何Form元素,则必须使用Invoke或BeginInvoke方法来完成.否则,您最终会进行跨线程操作.在.net的旧版本中,这会被不可预知的行为(取决于PC中的CPU核心)忽视,并且在以后的版本中,应该引发异常.