📄 foundvcerror.htm
字号:
<html>
<head>
<meta http-equiv="Content-Language" content="zh-cn">
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>如何定位程序占用CPU100%和挂起两种错误的代码出错位置---</title>
</head>
<body>
<p align="center"><b>如何定位程序占用CPU100%和挂起两种错误的代码出错位置------VC编程相关</b></p>
<p align="center"><b><a href="DemoDebug.rar">演示代码</a></b></p>
<p align="left"> <font color="#0000FF"><b>序言</b></font>:笔者是一个使用VC作为开发工具的程序员,在几年的
机房监控 和 系统集成开发 程序调试中,开创了一套错误定位的调试方法,因为此方法在我6年工作后的今天才创作出来,想必这个创作对很多小弟小妹们都有用,不可独享,就特拿来和大家分享。希望对大家有益。
机房监控系统开放源代码。</p>
<p align="left">
对于常规一些错误,使用调试模式调试,通常VC会检查到错误的位置,弹出错误终止框,根据VC的错误提示可以很快的定位错误代码的位置。然而对于程序运行过程中无故变成CPU占用100%后无响应,或者CPU正常,而程序无故变成无响应状态,对应两种错误,对于笔者以前的编程日子里,是一个很头痛的事情,很难定位,特别是程序的代码很多并且设计多个模块的情况时,更是难弄。然而,现在,笔者见到此类问题,能很快有效地找到问题的所致。</p>
<p align="left">
为了说明如何定位这两种错误的发生位置,这里我们提供了一个简单的演示程序,演示程序中给出了两个按钮,一个按钮按下后不久,程序即进入无响应状态(CPU正常),另外一个按钮则是点击后不久程序CPU占用几乎100%,程序无响应。为了更逼真,接近我们日常的调试的难度,演示程序中特将出错位置代码与点击按钮动作代码做了简单的分离。几个按钮只是简单修改一个整型变量的值,而在主框架的OnTimer中根据这个变量的值的不同做不同的处理,引发程序挂起:</p>
<p align="left">void CMainFrame::OnTimer(UINT nIDEvent) <br>
{<br>
// TODO: Add your message handler code here
and/or call default<br>
if(m_nChoice == 1)<br>
{<br>
LockToUnresponse();<br>
}<br>
else if(m_nChoice == 2)<br>
{<br>
LoopCPU100();<br>
}<br>
CMDIFrameWnd::OnTimer(nIDEvent);<br>
}<br>
</p>
<p align="left"> <b><font color="#0000FF">补充说明</font></b>:关于演示程序,需要说明的是,这个演示程序只是为了说明如何定位错误的问题,因此不是一个完整的程序,程序的某些代码不具有实际意义,一些变量和线程的初始化和释放代码不一定完全,因此,请读者不要仿效该演示程序代码的编码风格。</p>
<p align="left"> <b><font color="#0000FF">现象先睹</font></b>:在说明如何定位错误前,我们先来看看这个演示程序的运行效果:</p>
<p align="left"><img border="0" src="FoundV1.jpg"></p>
<p align="left">
点“挂起测试”或“CPU100%测试”按钮后,程序在3秒后及进入无响应状态,点前一按钮后的挂起与点后一按钮的挂起区别在于后一种方式挂起后系统的CPU占用马上上去了。挂起后程序无法接受鼠标和键盘的输入,对话框变白,无法刷新。</p>
<p align="left"> <b><font color="#0000FF">如何定位</font></b>:原理:程序没有响应,说明一个问题,主线程正忙或者被挂起,因此我们就是要定位主线程在无响应时已经执行到哪里了。</p>
<p align="left">
对于这两种情况的无响应的调试错误定位方法是相同的,首先我们使用按快捷键“F10”单步调试的方式启动待调试的程序,我们这里即指DemoDebug这个程序,注意,这里我们只按一下“F10”,程序即会停止在如下的程序位置:</p>
<p align="left"><img border="0" src="FoundV2.jpg" width="933" height="742"></p>
<p align="left">
对于所有的程序,按“F10”后都是停在这个地方,此时按VC的菜单“Debug->Threads”:</p>
<p align="left"><img border="0" src="FoundV4.jpg"></p>
<p align="left"> 线程列表中,目前只有一个线程,这个就是主线程,记下这个线程的ID,这里是00000f6c。</p>
<p align="left">
然后按“F5”继续调试运行,程序开始时是有响应的,点测试按钮以外的其他按钮、以及菜单都是有响应的:</p>
<p align="left"><img border="0" src="FoundV5.jpg"></p>
<p align="left"> 此时我们点两个测试按钮中的任意一个,程序在3秒后即变得无响应了:</p>
<p align="left"><img border="0" src="FoundV1.jpg"></p>
<p align="left"> 此时切换到VC,点VC的菜单“Debug->Break”,暂停程序运行:</p>
<p align="left"><img border="0" src="FoundV6.jpg" width="987" height="768"></p>
<p align="left">
暂停后,Debug下的Threads菜单变成可点击的黑色,点Threads,再次进入线程列表对话框:</p>
<p align="left"><img border="0" src="FoundV7.jpg"></p>
<p align="left"> 找到我们在上面已经记下的Thread
ID值00000f6c,找到这一行后鼠标双击这一行,VC即切换到该主线程的当前运行位置:</p>
<p align="left"><img border="0" src="FoundV8.jpg"></p>
<p align="left"><img border="0" src="FoundV9.jpg"></p>
<p align="left"> 在运行堆栈中,我们看到,有我们的自己写的代码段在堆栈中,双击“CMainFrame::LockToUnresponse()”所在的行,VC的上面代码框中将切换到CMainFrame::LockToUnresponse()函数的声明位置,这里我就可以看到在主线程中的OnTimer中常识锁定
m_mutexSingleWay,而该互斥锁正被另外一个线程CMainFrame::AnyThreadLoop(LPVOID lParam)锁定(或占用),所以主线程被挂起,这样,错误的位置就被这样找到了。接下来如何处理出错代码的问题已经超出了本文想讲的问题,在这里不再往下说明。</p>
<p align="left"> CPU
占用100%的问题的查找方法是一样的,先按“F10”的方式开始调试运行该程序,记下主线程的Thread ID,再按“F5”启动程序,点“CPU
100%测试”按钮,等程序没有响应后,点VC的菜单“Debug->Break”暂停程序,再点“Debug->Threads”,列出所有线程,根据记下的线程ID,找到主线程,双击该主线程行,VC此时切换到CPU
占用100%的线程的堆栈:</p>
<p align="left"><img border="0" src="FoundV10.jpg"></p>
<p align="left"> </p>
</body>
</html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -