我正在修改一个基于C#的UI,它与一个小型PIC单片机测试仪设备接口。
UI由几个按钮组成,这些按钮通过串行端口连接向微控制器发送“命令”来启动测试。每250毫秒,UI轮询串行接口,查找由PIC的测试结果组成的简短消息。消息显示在文本框中。
我继承的代码如下:
try
{
btr = serialPort1.BytesToRead;
if (btr > 0)
Thread.Sleep(300);
btr = serialPort1.BytesToRead;
if (btr > 0)
{
Thread.Sleep(300);
btr = serialPort1.BytesToRead;
numbytes = serialPort1.Read(stuffchar, 0, btr);
for (x = 0; x < (numbytes); x++)
{
cc = (stuffchar[x]);
stuff += Convert.ToString(Convert.ToChar((stuffchar[x])));
}
在最终读取串口之前,前三行包括三次调用BytesToRead和两次300毫秒睡眠调用的理由是什么?除非我错误地解释代码,否则从串口读取任何成功的内容都需要600毫秒以上,这对我来说似乎很奇怪。
答案 0 :(得分:4)
围绕SerialPort.Read()的行为是一个可怕的黑客攻击。它仅返回实际接收的字节数。通常只有1或2个,串口很慢,现代PC速度很快。因此,通过调用Thread.Sleep(),代码将UI线程延迟足够长的时间以获得Read()调用以返回 more 字节。希望所有这些,无论协议是什么样的。通常有效,但并非总是如此在发布的代码中,它没有工作,程序员只是任意延迟两倍。啊。
当然,最大的痛苦是UI线程在被迫睡眠时非常紧张。非常引人注目,绘画和响应用户输入的速度非常慢。
首先要注意协议,这需要修复。 PIC需要在其响应中发送固定数量的字节,因此您可以简单地将它们计数,或者让PC以某种方式检测是否已收到完整响应。通常通过发送唯一字节作为响应的最后一个字节(SerialPort.NewLine)或通过将响应的长度包含在消息开头的字节值来完成。具体的建议很难给出,你根本没有描述协议。
您可以保留hacky代码并将其移动到工作线程中,这样就不会对UI造成太大影响。您可以从SerialPort.DataReceived事件中免费获得一个。但这往往会产生两个问题,而不是解决核心问题。
答案 1 :(得分:0)
如果该代码最初处于循环中,则可能是等待PIC收集数据的一种方法。
如果你有真正的硬件要测试,我会消化你删除两个睡眠。
答案 2 :(得分:0)
@TomWr你是对的,从我读到的就是这种情况 您的评论下面的代码段:
try
{
// Let's check how many bytes are available on the Serial Port
btr = serialPort1.BytesToRead;
// Something? Alright then, let's wait 300 ms.
if (btr > 0)
Thread.Sleep(300);
// Let's check again that there are some bytes available the Serial Port
btr = serialPort1.BytesToRead;
// ... and if so wait (maybe again) for 300 ms
// Please note that, at that point can be all cumulated about 600ms
// (if we actually already waited previously)
if (btr > 0)
{
Thread.Sleep(300);
btr = serialPort1.BytesToRead;
numbytes = serialPort1.Read(stuffchar, 0, btr);
for (x = 0; x < (numbytes); x++)
{
// Seems like a useless overhead could directly use
// an Encoding and ReadExisting method() of the SerialPort.
cc = (stuffchar[x]);
stuff += Convert.ToString(Convert.ToChar((stuffchar[x])));
}
我的猜测与idstam上面已经提到的相同,基本上可能是检查设备是否发送数据并提取它们
您可以使用适当的SerialPort方法轻松地重构此代码,因为实际上有更好,更简洁的方法来检查串行端口上是否有数据可用。
而不是&#34;我正在检查端口上有多少字节,如果有什么东西,那么我等待300毫秒,然后再重复同样的事情。&#34;那是惨不忍睹的结局 &#34;所以是2次300毫秒= 600毫秒,或者只是一次(取决于是否第一次&#34;或者根本没有任何东西(取决于您通过此UI进行通信的设备,这可能是真的因为Thread.Sleep正在阻止UI ......)。#34;
首先让我们考虑一下你试图保留尽可能多的相同代码库,为什么不等待600毫秒呢?
或者为什么不只是使用ReadTimeout属性并捕获超时异常,不是那么干净,但至少在可读性方面更好,而且你直接得到你的String而不是使用一些Convert.ToChar()调用...... / p>
我感觉代码已经从C或C ++移植(或者至少是背后的理由)来自一个主要是嵌入式软件背景的人。
无论如何,回到可用字节数的检查,我的意思是除非在另一个Thread / BackgroundWorker / Task处理程序中刷新串行端口数据,否则我没有看到任何检查它的原因,特别是它的方式编码。
为了加快速度?不是真的,因为如果串行端口上确实存在数据,则会有额外的延迟。它对我来说没有多大意义。
使您的代码段更好的另一种方法是使用ReadExisting()进行轮询。
否则,您也可以考虑使用SerialPort BaseStream的异步方法。
总而言之,如果不访问其余代码库(即上下文),很难说。
如果您有关于目标/协议的更多信息,可以提供一些有关如何做的提示。否则,我只能说这似乎编码很差,再次脱离了上下文。
我将Hans提到的关于UI响应的内容翻了三倍甚至三倍,我希望你的代码片段在一个不是UI的线程中运行(虽然你在帖子中提到UI正在轮询我仍然希望该片段适用于其他工作者)。
如果它真的是UI线程,那么每当有一个Thread.Sleep调用时它就会被阻止,这会使UI不能真正响应用户交互,并且可能会给最终用户一些挫折感
也可以订阅DataReceived事件并使用处理程序执行您想要/需要的事情(例如使用缓冲区和比较值等)。
请注意,mono仍未实现此事件的触发器,但如果您针对普通的MS .NET实现运行,则无需多线程的麻烦就可以完全正常运行。
简而言之: