麻烦帮我看看问题所在,关于事件,线程等问题。

兵工厂三剑客 2016-05-11 11:05:27
先说下症状,我的程序在编译运行(或者直接把.exe文件打开)后,在第一次进行设备测试(主要是串口方面的)的时候,软件能正常运行,测试速度很快,且不出现bug。然后换一批设备(或者将第一次测试的设备进行再测试)进行第二次测试的时候,速度就很慢,就出现bug。
我是把串口信息显示在一个自定义文本框控件里面的。
第一次测试时,串口信息刷刷的就显示出来了(视角上来说一下就显示很多字符),显示速度很快,整个测试过程很流畅,界面也没卡。
第二次测试是这样的现象:文本框里面的串口信息加载得比较慢,视角上来说比一个一个字符进行显示稍微快一点儿。然后当进行到某个测试项的时候,就出现bug了。

报错信息如下:
************** 异常文本 **************
System.IndexOutOfRangeException: 索引超出了数组界限。
在 GDONUTextPlatform.MainForm.MethodUpdateGridDisplay(String data, Int32 sendcount) 位置 d:\VS2012\MyOwnPorjects\G DONUTextPlatform\GDONUTextPlatform\GDONUTextPlatform\MainForm.cs:行号 489

程序中自定义控件类库相关代码
public partial class GDResultGridDisplay: UserControl
{
public event TestStateChangedEventHandler TestStateChanged;
public bool IsCanStartTest
{
get { return iscanstarttest; }
set
{
iscanstarttest = value;
if (iscanstarttest && TestStateChanged!=null)
{
TestStateChanged(null, new TestStateChangedEventArgs(this, griddatasource));
}
}
}

}





下面是程序主界面一些相关代码:

private void MainForm_Load(object sender, EventArgs e)
{
this.ExtractSerialPortInfo += new InvokeExtractSerialPortInfo(MethodExtractSerialportInfo);
gdResultGridDisplay1.TestStateChanged += new TestStateChangedEventHandler(StartTest_Changed);
serialport.DataReceived += new SerialDataReceivedEventHandler(ReceiveSerialPortData);
}

private void ReceiveSerialPortData(object sender, SerialDataReceivedEventArgs e)
{
int n = serialport.BytesToRead;
byte[] buf = new byte[n];//声明一个临时数组存储当前来的串口数据
serialport.Read(buf, 0, n);//读取缓冲数据
this.BeginInvoke(new InvokeUpdateReceive(MethodUpdateReceive), buf);
}
private void MethodUpdateReceive(byte[] buf)
{
builder.Append(gb2312.GetString(buf));
//builder.Append(Encoding.ASCII.GetString(buf));//直接按ASCII规则转换成字符串
string data = builder.ToString().Replace("\0", "");
receiveShell1.WriteTextToReceiveArea(data);//追加的形式添加到文本框末端,并滚动到最后。
builder.Clear();
tempdata += data;
if (!gdResultGridDisplay1.IsOnTest)//没有在测试过程当中
{
if (ExtractSerialPortInfo != null)
{
ExtractSerialPortInfo(new GetMACOnlineInfoArgs(tempdata));//触发解析事件
}
}
}

private void MethodExtractSerialportInfo(GetMACOnlineInfoArgs e)
{
string data = e.SerialPortInfo;
省略一些无关的.
if (gdResultGridDisplay1.OnLineCount == gdResultGridDisplay1.EveryTestCount)
{
gdResultGridDisplay1.IsOnlineOver = true;
}
if (gdResultGridDisplay1.IsOnlineOver && gdResultGridDisplay1.IsScanOver)
{
gdResultGridDisplay1.IsCanStartTest = true;//触发开始测试事件,执行StartTest_Changed方法
}
}

private void StartTest_Changed(object sender, TestStateChangedEventArgs e)
{
e.IsOnTest = true;//设置成正在测试过程当中
gdResultGridDisplay1.IsCanStartTest = false;//不能开始测试
gdResultGridDisplay1.IsOnlineOver = false;//设置成False,防止上次测试未结束又开始新的测试
gdResultGridDisplay1.IsScanOver = false;//设置成False,防止上次测试未结束又开始新的测试
bool Flag = false;
thread = new Thread(new ThreadStart(delegate()
{
代码有点多,省略一些
this.BeginInvoke(new InvokeUpdateDisplayGrid1(MethodUpdateGridDisplay), tempdata, sendcount);
然后又是其他操作
}
thread .Start();
}




因为我也不知道问题出在哪里,所以就把测试过程中会执行到的一些主要代码贴上来了。大家耐心看看,帮我找下原因。谢谢啊。弄了快一天没找到原因。
...全文
145 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
兵工厂三剑客 2016-05-14
  • 打赏
  • 举报
回复
引用 9 楼 sp1234 的回复:
首先解决最基本的设计问题(也就是不应该有任何多余的阻塞语句的问题),然后要把不应该 BeginInvoke 的地方清除掉。例如你写的
this.BeginInvoke(new InvokeUpdateReceive(MethodUpdateReceive), buf);
为什么要把 MethodUpdateReceive 放到主线程去执行、去阻塞主线程呢?如果你连类似 builder.Append(gb2312.GetString(buf)); 之类的这些代码都没有信心在子线程中就能执行通过,那么可见你的代码会多么阻塞主线程。 第三,每当执行 serialport.Read(buf, 0, n); 之后,你不确定已经收到了消息结束符,不确定一条完整的消息恰好收到,因此不能立刻去执行 builder.Append(gb2312.GetString(buf)); 这类代码。你应该把收到的消息放到 List<byte> 或者 MemoryStream 结构里边,等到确定收到了消息结束符之后,把累计收到的消息(取出一条完整消息的那些字节)一齐取出来进行处理。 看前边的5、6条代码就知道有这么多非常严重的逻辑设计问题,后边的就不用多看了。
感谢P哥的耐心指点。 我会再去做下逻辑上的修改与优化。
兵工厂三剑客 2016-05-12
  • 打赏
  • 举报
回复
现在找到了问题的原因。 先描述一下。我将StartTest_Changed的线程中的某些地方做了处理,给足串口充分响应做出处理的时间。但是也最多只能测试5,6次,测试速度就又降下来了(CPU使用率一直居高不下,串口响应速度很慢),这个时候,StartTest_Changed里线程中给出的串口响应时间已经变得短暂了,串口还没做出响应就又执行后面的代码,就出现了“索引超出了数组界限”这个同样的BUG。 我描述的这个现象,是不是某些对象资源没有即使释放导致CPU使用率居高不下呢。 我观察了一下,第一次测试的时候,CPU使用率最高为30%左右,但是马上就降下去了(升-降-升-降这样反复),随着测试次数的增加,CPU使用率升上去后就一直将不下来,同时串口信息显示的文本框刷新速度变得很慢,此时CPU使用率最高维持到60%左右,就出现BUG了。 各位帮我分析下该怎么解决呢。
  • 打赏
  • 举报
回复
首先解决最基本的设计问题(也就是不应该有任何多余的阻塞语句的问题),然后要把不应该 BeginInvoke 的地方清除掉。例如你写的
this.BeginInvoke(new InvokeUpdateReceive(MethodUpdateReceive), buf);
为什么要把 MethodUpdateReceive 放到主线程去执行、去阻塞主线程呢?如果你连类似 builder.Append(gb2312.GetString(buf)); 之类的这些代码都没有信心在子线程中就能执行通过,那么可见你的代码会多么阻塞主线程。 第三,每当执行 serialport.Read(buf, 0, n); 之后,你不确定已经收到了消息结束符,不确定一条完整的消息恰好收到,因此不能立刻去执行 builder.Append(gb2312.GetString(buf)); 这类代码。你应该把收到的消息放到 List<byte> 或者 MemoryStream 结构里边,等到确定收到了消息结束符之后,把累计收到的消息(取出一条完整消息的那些字节)一齐取出来进行处理。 看前边的5、6条代码就知道有这么多非常严重的逻辑设计问题,后边的就不用多看了。
  • 打赏
  • 举报
回复
引用 5 楼 SCGH_Fx 的回复:
先描述一下。我将StartTest_Changed的线程中的某些地方做了处理,给足串口充分响应做出处理的时间。但是也最多只能测试5,6次......
我反复针对这类设计逻辑做过解释了,实际上假设你以为你给个“时间又足够充足、又足够短促的Sleep阻塞”,你这样纠结这个时间,这本身就说明你编写不了正确的程序。这种纠结必定会产生非常卡顿、CPU和内存资源消耗极高的结果。 一个正确的程序,不需要一丁点阻塞,连Sleep(1)都不需要写,更何况所谓“充分响应时间”这种故意阻塞呢?从删除这种阻塞开始入手,你就能知道如何写好通讯程序了。
兵工厂三剑客 2016-05-12
  • 打赏
  • 举报
回复
引用 6 楼 baidu_24214803 的回复:
1.工具栏里面有个分析->启动性能分析向导。把你的.exe分析运行一下,生成一个.vsp的分析报告。然后把这个报告导出来,看一下是哪些操作占用你的内存资源。 2.把一些不必要的操作或者对象省略掉。 3.你的自定义文本框有没有及时清空,比如你开始第二次测试时,你应该把文本框清空,释放一些资源。如果不释放,测试次数增加后,占用的资源越来越大,肯定会卡的,显示也就越来越慢了。 这个是分析。
按照你的提示,我将文本框清空后真的解决了。
baidu_24214803 2016-05-12
  • 打赏
  • 举报
回复
1.工具栏里面有个分析->启动性能分析向导。把你的.exe分析运行一下,生成一个.vsp的分析报告。然后把这个报告导出来,看一下是哪些操作占用你的内存资源。 2.把一些不必要的操作或者对象省略掉。 3.你的自定义文本框有没有及时清空,比如你开始第二次测试时,你应该把文本框清空,释放一些资源。如果不释放,测试次数增加后,占用的资源越来越大,肯定会卡的,显示也就越来越慢了。 这个是分析。
兵工厂三剑客 2016-05-11
  • 打赏
  • 举报
回复
引用 2 楼 songbing774933 的回复:
System.IndexOutOfRangeException: 索引超出了数组界限。 在 GDONUTextPlatform.MainForm.MethodUpdateGridDisplay(String data, Int32 sendcount) 位置 d:\VS2012\MyOwnPorjects\G DONUTextPlatform\GDONUTextPlatform\GDONUTextPlatform\MainForm.cs:行号 489 这么明显的异常信息啊, 索引超出了数组界限 查看MainForm.cs:的489行的代码
出现异常是在第二次测试的时候,第一次测试是正常的。我看了,是因为data的问题,因为第二次测试串口信息加载得慢。我在这句之前用MessageBox弹出阻塞一下测试就能通过。 问题还是在为什么第一次测试是正常的,速度很快;第二次测试时速度就慢下来了,然后报这个错误。
兵工厂三剑客 2016-05-11
  • 打赏
  • 举报
回复
可能会有歧义。我补充下。 第一次测试是正常的,就是不报错,整个测试过程都能按照逻辑顺序顺利完成。第二次测试的时候才报“索引超出了数组界限”这个错误。
songbing774933 2016-05-11
  • 打赏
  • 举报
回复
System.IndexOutOfRangeException: 索引超出了数组界限。
在 GDONUTextPlatform.MainForm.MethodUpdateGridDisplay(String data, Int32 sendcount) 位置 d:\VS2012\MyOwnPorjects\G DONUTextPlatform\GDONUTextPlatform\GDONUTextPlatform\MainForm.cs:行号 489


这么明显的异常信息啊, 索引超出了数组界限

查看MainForm.cs:的489行的代码
兵工厂三剑客 2016-05-11
  • 打赏
  • 举报
回复
没人吗。来人帮我看看啊。

110,538

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧