Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/301.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# System.IO.Ports.SerialPort和多线程_C#_.net_Performance_Serial Port - Fatal编程技术网

C# System.IO.Ports.SerialPort和多线程

C# System.IO.Ports.SerialPort和多线程,c#,.net,performance,serial-port,C#,.net,Performance,Serial Port,我有一些串行端口代码,需要不断地从串行接口(例如COM1)读取数据。但这似乎是非常CPU密集型的,如果用户移动窗口或在窗口中显示大量数据(例如通过串行线接收的字节),那么通信就会混乱 考虑到以下准则: void port_DataReceived(object sender, SerialDataReceivedEventArgs e) { byte[] buffer = new byte[port.ReadBufferSize]; var count = 0; try { cou

我有一些串行端口代码,需要不断地从串行接口(例如COM1)读取数据。但这似乎是非常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);
}
由于数据流是恒定的,因此需要收集代码 并且必须对数据进行分析(帧/数据包)

如果没有用户交互,也没有向窗口添加任何内容, 这很好,但一旦有了互动,沟通就真的变得一团糟。 发生超时等

例如,这会把一切都搞砸:

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);
});
但即使是对log4net的简单调用也会导致问题

是否有优化串行端口通信的最佳实践 或者有人能告诉我我做错了什么

更新:

以防上述情况没有多大意义。我举了一个非常简单(而且愚蠢)的小例子:

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);
    }
}
结果可能如下所示:

private void port_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
        var port = (SerialPort)sender;
        while (port.BytesToRead > 0)
        {
            int byte_count = port.BytesToRead;
            byte[] buffer = new byte[byte_count];

            int read_count = port.Read(buffer, 0, byte_count);

            // PROCESS DATA HERE

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

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


这可能是导致我出现问题的原因之一吗?

您应该重写port_DataReceived过程以读取数据,直到port.BytesToRead大于零,如下所示:

private void port_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
        var port = (SerialPort)sender;
        while (port.BytesToRead > 0)
        {
            int byte_count = port.BytesToRead;
            byte[] buffer = new byte[byte_count];

            int read_count = port.Read(buffer, 0, byte_count);

            // PROCESS DATA HERE

        }
}

另外,我建议您只需在过程端口_DataReceived的队列列表中插入数据,并在单独的线程中执行数据处理。

您的最后一个结论,即事件在主线程上运行,对于Windows App可能不正确。不要在控制台中测试这一点

正确的调整方法是:

  • 设置足够大的缓冲区,尽管最小4096通常是可以的

  • 将ReceivedBytestThreshold设置为允许的最高值(并在打开()之前执行

  • 在收到的事件中尽可能少地执行,传递 如果需要更多时间,请将数据发送到队列或MemoryStream


    • 经典的解决方案是使用FIFO缓冲区。确保FIFO的大小足够大,以处理任何有大量输入且占用处理器块的关键情况

      您甚至可以有一个双缓冲系统:

      --->|Reader|-->FIFO-->|Processor|--->FIFO2--->|Displayer|
      

      我很惊讶没有人发现这一点。SerialPort类在使用DataReceived事件时使用自己的线程。这意味着,例如,如果订阅者正在访问任何表单元素,则必须使用Invoke或BeginInvoke方法进行访问。否则,将以交叉线程操作结束。在较旧版本的.net中,这会导致不可预知的行为(取决于PC中的CPU内核),而在较新版本中,会引发异常。

      调用的最后一段代码在哪里?“data”变量来自何处?当IDataCollector引发事件以指示它收集了完整的数据帧时,将调用上面的代码。它在WPF窗口中。但即使在IDataTransportServer或IDataCollector实现的代码中添加log.Warn(“…”)语句,串行通信也会出错。。。“数据”只是我们从串行行中读取的数据:buffer.GetSubByteArray(0,count)几乎任何代码都会导致串行通信出错…在port_DataReceived()函数或port_DataReceived()中没有调用的地方等待?我在最初的文章中没有写过几个重要的东西:-port_DataReceived是回调函数,它在自己的线程中执行。只要COM端口上写入了一些数据,就会调用它。但是,如果新数据写入COM端口,并且您仍然没有从上一次调用返回,即处理函数内部的数据,那么对port_DataReceived函数的调用可能会失败,这就是在您离开函数之前检查port.BytesToRead属性的重要方式。+1,虽然我不确定这是否回答了问题,这是一个重要的信息,在过去让我有点痛苦。