C# System.IO.Ports.SerialPort和多线程
我有一些串行端口代码,需要不断地从串行接口(例如COM1)读取数据。但这似乎是非常CPU密集型的,如果用户移动窗口或在窗口中显示大量数据(例如通过串行线接收的字节),那么通信就会混乱 考虑到以下准则: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
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,虽然我不确定这是否回答了问题,这是一个重要的信息,在过去让我有点痛苦。