首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >System.IO.Ports.SerialPort与多线程

System.IO.Ports.SerialPort与多线程
EN

Stack Overflow用户
提问于 2009-06-10 04:03:25
回答 4查看 29.3K关注 0票数 4

我有一些SerialPort代码,经常需要从串行接口读取数据(例如,COM1)。但是这似乎是非常密集的CPU,如果用户移动窗口或大量数据显示到窗口(例如通过串行行接收到的字节),那么通信就会受到干扰。

考虑到下列守则:

代码语言:javascript
复制
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);
}

需要收集代码,因为数据流是常量的,数据必须分析(帧/包)。

代码语言:javascript
复制
    port = new SerialPort();

    //Port configuration code here...

    this.collector = dataCollector;

    //Event handlers
    port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
    port.Open();

如果没有用户交互,也没有向窗口添加任何内容,这就可以正常工作,但一旦交互发生,通信就会变得一团糟。超时等..。

例如,这一切都搞砸了:

代码语言:javascript
复制
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的简单调用也会导致问题。

有没有优化SerialPort通信的最佳实践,或者有人能告诉我我做错了什么.

更新:

万一上面说的不是很有道理。我举了一个很简单(也很愚蠢)的小例子:

代码语言:javascript
复制
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);
    }
}

结果可能如下所示:

监听COM1: 6 Client on COM2: 10这是我发送的一些示例数据->6,这是我发送的一些示例数据

所以从端口读取数据发生在主线程上.

这可能是造成我问题的原因之一吗?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2009-06-10 18:13:24

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

对此进行调整的适当方法是:

  • 设置了足够大的缓冲区,尽管最小值4096通常是Ok
  • 将ReceivedBytesThreshold设置得尽可能高(并在之前打开()
  • 在接收事件中尽可能少地执行,如果需要更多的时间

,则将数据传递给队列或MemoryStream >

票数 4
EN

Stack Overflow用户

发布于 2012-11-20 22:49:31

我很惊讶没人发现这个。SerialPort类在使用DataReceived事件时使用自己的线程。这意味着,例如,如果订阅服务器正在访问任何表单元素,则必须使用调用或BeginInvoke方法进行访问。否则,您将得到一个跨线程操作。在较早版本的.net中,这可能会导致不可预测的行为(取决于PC中的CPU核心),而在较晚的版本中,则会引发异常。

票数 15
EN

Stack Overflow用户

发布于 2009-06-10 15:04:45

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

代码语言:javascript
复制
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

        }
}

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

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/973631

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档