一个诡异BUG引发的血案(线程死锁造成的CPU利用率逐渐增高)

        我首先声明,我有一些标题党的嫌疑,确实没有什么血案发生,顶多是我找BUG 时由于太用力把嘴唇给咬破了。

        这个故事发生在刚刚过去的猴年春节前夕,公司准备上传最后一个包到appstore,然后大家各回各家各找各妈。大家憧憬着即将到来的假期写着代码,以完成最后一个版本。由于这时候程序猿的春天即将到来,内心总是蠢蠢欲动的,代码自然而然的写的有点糙。在测试即将完成进入性能测试步骤时,发现手机在使用一段时间后会发烫,并且程序卡顿的要死。由于这种现象是在一段时间后才发生的,所以一开始谁也没有在意,当问题暴漏的时候,已经距离发版日期很近了,也就是说如果我们不能在一周内解决那就要放弃这个版本,或者放弃过春节。

        小伙伴们都懵逼了,大家开会自查代码,优化性能,利用instument里的找问题。发现了很多代码写的不合理的地方,一一改正,这里我必须说一下,作为一个程序猿是要有猿类的觉悟的,可能我们生活中不太注意形象,作风邋遢随性,形象无法登大雅之堂,但是代码从我们手中写出来绝对不能乱七八糟,敷衍了事,相信每一个自律的程序员对自己的代码都有一定的要求,代码一定要遵从自己心胸中认定的最熟悉的规范。千万不能有个侥幸心理什么的,有个定律叫墨菲定律,意思是该发生的一定会发,出来混迟早要还的。

你少写一个判断,那么一定会有崩溃发生在哪里,影响着软件的质量。如果你少写两行注释,那么很久以后当你自己也读不懂这些代码的时候会从心里鄙视“他*的,这谁写的代码,这么乱”,全然想不起自己鄙视了自己一番。

         说太多了,干货在这里。我们优化了许多代码以后,虽然有些改善,但是软件过一段时间后依然会卡顿无比,奇怪的是内存并未上涨,多次检查过发现并没有内存泄漏。

用instument里的 time profile工具查看发现CPU的占用率是随时间增加而逐渐上升的,于是小伙伴们一看不是常见问题纷纷束手无策。这个时候作为一个团队拿工资最高的家伙,解决这些疑难问题简直是义不容辞啊。于是我开始了解决问题的漫漫征程,之所以说称之为漫漫是因为解决问题一共花了3天,加上期间公司年会和部门年会的两天无心干活的话,那一共花了5天。

        我思考了一下问题表现和可能发生的情况,发现这种情况app的内存不涨,CPU逐渐升高,那么问题一定是出在线程上,利用工具检查后印证了我的想法,我发现程序的线程数一直在涨涨涨,线程数一多,CPU处理主要逻辑时wait 的时间就会变长,cpu的运算被浪费在等待那些无用的增多的线程中去了。但是无奈的是我怎么看怎么看不出到底为何会涨。于是我要求小伙伴们自查代码以及互相review代码,看看有没有滥用线程的情况,查了整整一天直到晚上11点,最终无果。

         第二天我无奈的发现我只有一种方法来找到我想要的东西,当所有的办法都不管用,那就只剩下排除法了。因为上一个版本还没有类似的问题,那么我只要注释掉这次迭代的代码,一点一点地打开注释,用排除法找到出问题的代码。悲催的是BUG虽然是必现的但是由于每次需要半小时才能出现明显的卡顿现象,那么排除法查找问题就变的奇慢无比,这真的是一种折磨啊!最后春节将至,小伙伴们开始陆续提前放假,我只好一个人孤独的寻找问题。

          第三天当我找到出问题的代码后,我无论如何也不能相信,这段代码可以导致CPU利用率过高,而且这段代码根本看不出和线程有什么关系。我把代码贴出来就在下面,当时我第一反应是随机数函数和获取时间戳代码会耗时么?难道编译器出问题了?于是我又写了个Demo把这段代码分离出来测试,发现居然没有任何问题,不会出现任何导致CPU利用率上升的问题,而且他*的根本没用线程啊?

-(void)resetTimeSign
{
    NSString * Factor =[NetConfiguration sharedNetConfig].factor;
    NSString* timeStamp=[NSString stringWithFormat:@"%.0f",[NSDate date].timeIntervalSince1970*1000];
    NSString *text= [NSString stringWithFormat:@"%@_%@_%@",Factor,self.userItem.userID,timeStamp];
    [[NetConfiguration sharedNetConfig] addCommonRequestHeaderValue:timeStamp forKey:kDefault_Cleartext];
    int arc =arc4random()%900+100;
    NSString *cipherText = [NSString stringWithFormat:@"%d%@%d",arc,[[NetConfiguration sharedNetConfig] getReverseString:text],arc];
    [[NetConfiguration sharedNetConfig] addCommonRequestHeaderValue:cipherText forKey:kDefault_Ciphertext];//--cyc
}
         当我意识到可能是这个单列的问题时已经是第四天了,这时候年会过完,抽奖抽到了一个键盘,心说果然是程序猿的命啊!借着好心情我又分析了一下,这几行代码,如果有问题,肯定在这几个单列里。于是我终于开始从头看这个单列,看了一眼,顿时开始问候无数次某人女性先祖,单列是这么写的

+ (UserManager *)shareUserManager
{
    if (shareUserManager != nil) {
        return shareUserManager;
    }
    @synchronized(self)
    {
        if (!shareUserManager) {
            shareUserManager = [[UserManager alloc] init];
        }
    }
    return shareUserManager;
}

          当看到@synchronized的时候,我终于明白出了啥事情,我们有个某线程轮询操作,每次都生成一条新线程,然后销毁,然而这个东西一调用和另一处锁造成了死锁,于是线程越来越多,无法销毁...... 死锁的线程参与分配CPU时间片,于是主线程wait的时间越来越多,越来越卡。

          这年头单列早就不这么写了好吧,单列如今怎么写我也不在这里鳌述,这次的问题解决后我明白了一个道理,工具并不总是有用的,有时候走投无路的时候,排除法这种笨办法反而能解决问题,而且为了快速找到问题,我借鉴了二分查找的思想来做排除法,觉得能解决问题的办法就是好办法。总之黑猫白猫,能逮到老鼠就是好猫。当然解决问题一定有更好的办法,如果你看了以后觉得有好办法,希望大神可以不吝赐教!




  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值