那些导致程序抽风的神秘原因

       作为码农,写出的程序难免会遇到一些神秘地假死,异常,溢出等种种抽风症状。往往过一会儿再尝试又好了,或者关了在开又好了,别以为真的好的,它总会在在某个未知的时刻继续抽风,无休止地折腾。 本文就专门记录我遇到的程序抽风问题,以及解决办法。持续更新中……

      1. sqlserver插入数据过慢、假死。

          在开发中时不时遇到这个问题,一开始我觉得是程序有问题,那该页涉及到的几千行的代码统统检查了一遍,确实改了很多东西,但问题还是可能出现,后来听其他同事也提到这个问题,当时就奇了,经过痛苦的探索,终于发现,原来我们用公共的数据库,有个哥们儿在代码里面开始了一个事务,然后就断到事务里面调试,这就是玄机所在了。

      2. 某次用在C++,c#混合开发,在C++里面CallBack C#的函数,在debug的时候一切正常,但是,独立运行程序就不行了,异常不断。因为C++的程序里面调用了一些很底层的东西,所以就尝试寻找debug 和直接运行的差异,发release版,修改程序兼容性,悲剧的事,好像每改一次都有效果,还没有来得及欢呼,抽风症状就发生了,于是继续折腾。程序运行起来后attach进程往往会提前抛异常,定位不到出错地点。后来终于发现了那在C++某个参数是LPSTR,指向一个数组TCHAR[1024],未付初始值,传给C#,对应C#的StringBuilder,这个StringBuilder的Capacity属性应该是1024位,我们可以给这个StringBuilder重新写入一些值,这样在 C++ 里的那个LPSTR  就有值了,问题是,这个未付初始值的 LPSTR,在debug的时候传给StringBuilder,它是1024位,估计debug时数组初始化的方式比较特别,但是,在独立运行的时候就未必了,因为未赋初始值,那个数组什么值都可能,完全有可能是'\0',这样得到的,StringBuilder的Capacity就小于1024。因为偶尔StringBuilder用到的长度小于得到的长度,这就没有异常,一旦StringBuilder用到的长度大于得到的长度异常就出现了。解决办法,赋初始值。如果ZeroMemory来赋值,那StringBuilder得到的Capacity值就是0了,必然出错。

    3.困扰一个半月,直到大年三十才解决的问题,不过至今不知道原因。

       还是涉及到c#与c++交互的问题,在c#里面有几个函数,需要c++来调用,可是系统运行一会儿,就会卡死,神奇的是这个“一会儿”可能长,可能短,短的话20秒,长的话,一直不出错。出错后,如果用任务管理器强制结束进程,还会弹一个异常stack overflow。有时候强制结束进程后,那个进程在任务管理器中不见了,但是ide仍然处于debug状态,无法停止,一点击它就提示visual studio忙,也被卡死了,只有用任务管理器强制结束visual studio ,说说的调试过程。

     (1)一开始觉得 stack overflow 肯定是c++那个地方发生了溢出,于是把每行 c++的代码都检查了一遍,这就耗了两周,改了很多东西,但是问题依旧。

     (2)因为有部分代码是从MFC中剥离出来的,用CString的地方有点儿多,所有所以在网上找了一个牛人写的StdString类来代替 CString,它实现了CString的所有方法。因为之前就发现这个类本来有些不完美,只要用了StdString涉及到大小写转换的地方在程序结束时就会报异常,例如CompareNoCase、MakeLower、MakeUpper,所有现在系统不能稳定,我就把矛头指向了StdString这个类,又花了一周的时间,把所有涉及到StdString的的地方都改写,彻底抛弃了StdString。悲剧的是,经过了这样的折腾,它丝毫不被我感动,问题依旧,耗时3周了。

      (3)屏蔽调试,其实这个工作一直在进行,因为问题不好重现,屏蔽了一段代码感觉运行起来了,但不能真正确定是这段儿代码的问题。所以只有先找其他的问题,现在已经无计可施了,所以就继续来屏蔽调试。又经过一周的探索,终于锁定的问题。 发现某一个全局的回调函数指针 F 执行多次后就会不响应。以后就集中力量解决这个问题。耗时4周了。

       (4)这个函数指针指向的是c#里面都某个函数。但是这个问题就在于,这个函数指针没有任何特别的地方,那为什么系统对其他的指针都放行,专门针对它呢。在古龙的小说里面,最厉害的杀手,往往外面最普通,如一粒米,混在一缸里米里面,毫无特点,没有特别的名字,没有特别的外表,然后他就关键时刻使出杀手锏。现在的情况就是,这个毫无特点函数,阻挡了我的路。它如此神秘,没有特别的名字,我特地重命名过,问题依然;没有特别的逻辑,我特地将函数体的内容直接 return,问题依然;没有特别的参数,就两个 LPCSTR,对应的在c#里面的string,而其他函数也有这样的参数,运行毫无问题,而就它自己来说,它自己也有可能运行毫无问题,偏偏偶尔出点儿问题,要了人命了。 综上:情况就是,只要调用哪个函数指针,程序就可能错,彪悍bug不需要解释。 探索到这些东西,又耗了我差不多一周。耗时5周了。

      (5)网上有很多关于c#和c++交互调用的案例,但问题都跟我的不一样,说的大多是垃圾回收的原因引起的问题,而这不可能是垃圾回收的问题,所以的涉及到与c++交互的东西都已经是成员变量了,不会被垃圾回收给回收掉的,这点儿我还是有信心的,不过看得多了,终究信心有点儿动摇了,就死马当作活马医了,考虑是否是其他地方垃圾回收,造成内存地址的移动呢,于是造成传到c++的函数指针已经执行错误的地址了,想是这么想的,但这是这不可能。果然,等我用GCHandle.Alloc把所能想到的地方都包裹了一下,问题依旧。 不过经过探索还是学到了一点儿:我的函数指针是类的成员变量,只要这个类的生命周期没有结束,类里面所有的成员变量都不会被回收。也不用担心内存移动的问题,对于大对象,如类class,gc是不会移动它的。此时已经耗时 6周。

        (6)我多次考虑放弃了,差不多是时候了,找替代方案吧。就一个函数不能调用耗我一个半月来解决,实在太亏了。问题是还解决不了,前功尽弃有点儿不甘心。突然我想到,我只是要解决问题,人们都想通过找到问题的原因来解决问题,但是找不到原因就不能解决么? 未必啊,于是,我把注意力集中到解决问题上来了,我想,如果对那个函数调用不成功,我就不调用了,直接对它的效用好了,于是我增开了两个线程,一个负责调用哪个函数,一个负责监视那个调用,如果超过500毫秒,我就直接把调用线程终止。不管怎么样,主线程继续跑,不会卡死。这样弄了一番,系统终于正常了。  

       这次,我用了很BT的办法,打败了超自然力量。

4 .待续……

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值