越维护代码越感觉心惊肉跳的

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/baijiaheizhiganmao/article/details/50824476

       很奇怪,今天一大早来到公司我就有这种感觉。大概我感觉昨天我改正的那个bug其中可能还隐含了“杀气”!然后我仔细的再次阅读了一遍,  果然,里面隐藏了一个不定时“炸弹”——有一个读文件操作,我没有添加条件判断语句。虽然说要读取的这个文件,使用该产品的用户是几乎没可能会去手动查看的(里面就一串随机的序列号,做标记值使用),但是,不排除出现意外,导致程序读取不成功。然后我打开后,直接使用文件 句柄写,就可能由于打开不成功导致写的时候程序崩溃!!!看来维护了这么久的代码,察觉意外的感觉还是多少有点的。然后我“心惊肉跳”的感觉确实是今天才有的,借此机会,总结一下个中原因,跟大家说说,希望大家的代码中不会出现再让自己活着别人产生这种不愉快的感觉。
        目前程序中的bug,基本可基于这些原因产生一个大致的分类:线程、进程用完后对资源没有快速的释放;产品升级导致前一个版本的部分文件在新的版本中不兼容;内存溢出;产品运行时内存不足,导致用户卡。
        一、线程、进程用完后对资源没有快速的释放
        这个问题不太好总结,先说说出现问题的场景和现象吧。有个产品,能够以列表的形式列出一个相关主题的所有文件,这些文件都是网络上抓取的,以“文件名.pdf”形式排列在列表中,用户点击某个文件名后,程序会启动下载线程,下载完毕会调用系统的读取pdf格式的软件打开该文件,同时下载后会放置到一个指定目录:假如用户没有指定目录,放到一个默认目录中,假如用户指定了目录,则将这部分文件下载完后移动到指定目录,然后打开。实际情况是这样的,文件被放置在一个反url形式的目录下:如url为:
https://www.baidu.com/s?ie=%B2%E5%98%9B7,那么可能会被放置在这个目录:“……/B7/89/E5/B2/iE/utf-8格式文件名.pdf”。然后你去代码中查找原因,逻辑都没错,但是在log中发现了这样一条记录:文件XXXX 移动到XXXX 失败,MoveFileEx返回值[32],然后百度了一下这个返回值发现:“〖32〗-进程无法访问文件,因为另一个程序正在使用此文件。”问题已经找到了,那么是谁还在抓着这个资源不放呢?原来,此处有两个线程一起工作,  线程A下载和打开,线程B移动文件。显然,在移动文件的时候线程A还试图打开这个文件,B线程失败了,但是程序必须要放在一个位置啊,系统就在当前程序 家目录上以url派生了一个目录默认放置。如何解决:将打开文件的操作移动到线程B中,A下载完后就不再负责其他事情。总结一下这个问题:资源释放,线程同步等基本功,之前写代码的都没有意识到;测试的时候也不彻底,导致后期徒增工作量。
        二、产品升级导致前一个版本的部分文件在新的版本中不兼容
        也还是先来描述一下这个的场景和现象。产品X,目前升级到了2.0,但是用户有一些配置文件是1.0时候配置好,并保存在本地的。升级完发现,这些配置不起作用了!现象就是:程序加载完配置文件后,一直在loading配置项。详细查看了一下代码,发现:1.0版本的配置文件不需要增加配置项的后缀,而2.0版本必须有后缀才可以读取。怎么办?为了不让用户流失,添加一个模块,让程序启动过程中依次去读取用户的配置文件,并将文件内容上传服务器,在服务器那边给配置项匹配上合适的后缀,写回到配置文件。总结一下,该问题出现了,我觉得多少算是合理的。只是在打算升级2.0前大家都没有全面的检查程序的功能模块,导致了用户使用上的不便。
        三、内存溢出
        这个分类产生的问题,可多了去了。比如说,你以为你定义好的线程池是可以从服务器上接受那些数据的,但是,你没想到,客户一次运行了10天半个月,结果某一天就“爆”了!然后看dump文件:不对啊,不应该啊!后来找用户一问:原来如此。改呗!或者说,有一个字符串,我定义死了就应该是17个字符在里面,用来保存从文件中读取的内容(文件里面只有一行且是16个字符)。读出来后,for循环按下标依次赋值个另一个字符串,但是中途程序break了!debug的时候发现,为什么读出来的是5个字符,这样我for16次不break才怪!然后查看这一串逆天字符明明看到16个却只能读取出5个的原因:这一串字符是utf-8格式的,里面有一个中间有一个编码是换行,结果你按行读取的时候只读取到了前半部分……夭寿啊!诸如此类。总结一下原因:这类问题我觉得不要过于苛责。首先要能将显然的内存越界问题改正,在读取内存块 的时候要将读取的内存做一次检测,看和想象中的是否相符,使用了这些手段,做一个基本的保证。将来要是还出现了这个问题,就真的不是人在一开始可以解决掉,只能在发现问题的时候改正问题。
        四、产品运行时内存定死,导致用户卡
        场景:产品XXX,该产品会抓取一些地区符合某类特征的信息。但是不同地区这些信息所需要的消耗的内存是不一样的,版本1中将这个内存消耗定死了,
    比如我就给1个8M的buffer去接受,导致用户卡着。
        解决方案:根据不同地区的不同情况,动态调整改内存大小。
        总结:这个没有总结,毕竟这种情况是不多见的。只能说我们遇到一些很奇葩的客户,他们的电脑很老,操作系统版本很低。不舍得加内存。我当初是想
    直接将这个buffer扩大几倍就行了,但是有了这个实际情况,还真是必须通过动态调整才能让用户用的舒服一点。

        最后是一些其他问题总结了,比如说,你代码中有主动去请求某项资源的操作,那一定要判断一下请求是否成功,比如我早上发现的“杀气”。还有,不要随便使用return,
特别是一个void函数,我就看过一个void函数,里面不止一个return啊,我连哪里返回了都不知道!!!最后,程序中尽量多一些log,多一些捕捉异常的语句,if——else一定要配对使用(哪怕你的else里面是空,也要有一个),诸如此类。
        就写到这里吧,希望对你有帮助。

展开阅读全文

感觉程序员应该是越老越吃香。。

05-20

技术的本质是进化,任何技术不可能凭空出现,所有技术都是再现有的技术上进行改良和进化。就像学过C++,对C#和JAVA上手也很快。。编程语言有太多的相似点了。。一个架构可以有多种语言版本,因为设计思想是跨语言的。优秀的设计思想是需要经过长时间的积累和考验的。rn最近公司要把一个端游移植到手游平台上。。。一个在端游混迹了十年的老程序员,C++和C#都有7,8年经验。。公司另外一个端游就是他当年带领开发的。。使用的是unity技术。unity是新技术。。他熟悉了一个月,短短2,3个月就移植过去了。。我看了代码,代码和架构都重新优化的很好,把以前不足的地方进行了优化,试问有多少新手程序员能做到。而新来的unity客户端程序员,写哥简单小游戏都BUG百出,设计思想需要长时间的经验积累和感觉。rn以前一个十年的服务端程序员合作写一个大型网络游戏游戏,感受他设计的架构。简练和稳定,没有长时间的编程积累我觉的不可能做到。。。。。rn感觉像阿里或者腾讯那种服务端架构,没有十年以上磨练经验的程序员不可能设计的出来。。。rn如果是混日子的程序员,当然只是吃青春饭。。但是如果优秀,上进,热爱自己事业的程序员,我觉的应该是越老越吃香。。 论坛

写了多年程序,现在感觉,越写越懵了~~~~

02-26

从写第一个程序到现在应该快7年了吧。。rn大多数完成的程序都是给公司或者学校使用的商用程序,数据库,工业控制,游戏,网站。。。。rn语言从最开始入门的VB,C,到现在使用的C++,ASP.NET。。。。。rnIDE现在一直用的是 C++ Builder VS.NET VB(VB很多年不用了。。)rnrn但是最近一段时间,在写程序的时候,感觉越写越懵了。。。。感觉太“杂”了。。。rnrn“杂”是什么意思呢??rnrn比如,我现在在 WINCE 上开发一个工业控制程序,使用VC2005 MFC(已经写完了)。在写的时候,我就非常别扭(这种感觉以前就有,只不过在我“闲”下来的时候非常强烈而已)。rnrn例如,我现在要去对一个文件进行读写操作。还没动手写,脑子里就冒出N个方法:rn1.使用 C 函数:fopen,fclose,fread,fwrite.....rn2.使用 C++ 函数:fstream,ifstream,ofstream......rn3.使用 MFC 库函数:CFile....rn4.使用 win32 API:createFile,readfile,writefile....rnrn然后,想半天。。。。。rn最后,随便选一个写。rnrn往往在我的同一个程序中,在 A 处读写文件用的是 C 函数,在 B 处读写文件用的是 C++ 函数。。。。rnrn算法也是一样,碰到一个问题,脑子里冒出N个算法。。。。rnrn总之,这个“杂”,“乱”,在我写程序的过程中挥之不去。。。rnrn回想一下,可能是长期在公司写程序,烙下的印记。。。rnrn公司要什么??rn1.快。。。。“你这个项目做了快一个月了,还没写完吗??还有一个星期,不管你用什么办法,给我完成它,否则XXXXX。。。。”rn2.快快。。。。。rn3.快快快。。。。。。rnrn于是我。。。。不择手段,不讲究方法,没时间多想,没时间琢磨。。。。rn不择手段,可能是我写程序的座右铭吧。。。同事都说我变态。。。。rnrn我在公司写程序是以快出名的。。别人做一年,我做2个月,别人做一个月,我做两星期。。。rnrn但是,我不想就维持现状,我想提高。。。rn谁能帮我解决这个问题???rn我要怎么做??? 论坛

為何越愛越孤獨

07-24

愛情與孤獨之間﹐存在著非常奧妙的關係。rn有人因為害怕孤獨而選擇愛情﹐不管那個愛情的品質是否合格﹔有人在愛情中越來越感到感到﹐最後﹐愛情只成了壓在胸口上的責任﹔也有人因為害怕孤獨而害怕愛情的牽牽扯扯﹐怕愛成為自由人生的絆腳石﹐只好逃走﹔有人得到愛情之後﹐報怨情人讓他不時咀嚼孤獨﹕“可不可以多陪陪我﹖”rn有了愛情﹐真能逃避孤獨嗎﹖rn我想﹐不能。人生中有人相依的感覺真好﹐但是畢竟沒有一個肩膀﹐可以真正無怨無悔讓你隨時隨地﹑大事小事往那裡倚靠。愛情的魔力﹐確實會使我們許願成為心愛的人的倚靠。但過重的倚靠力量﹐往往使我們的肩膀發疼﹐無法繼續承擔。rn耐心被糾纏消磨後﹐誓言往往成了悔不當初的記憶。rn沒有人能承擔天長地久的倚靠﹐在時光流逝中﹐倚靠你的人﹐總會成為你的人生壓力。這時﹐你的孤獨和逃離成了迫切需要。rn也許﹐即使是世人看來都美好的事﹐當事人心裡﹐可能則是另有一番滋味在心頭。rn如果愛情與愛情帶來的責任﹐完全侵佔了我們所需的孤獨的自由﹐那麼﹐生活也比監獄好不了多少。rn佛洛姆說過一句愛的箴言﹕“唯有能在孤獨中自處的人﹐才能掌握愛的藝術﹐不致在愛的旋渦中幻滅與迷惘。”rn能在孤獨中自處的人﹐能給自己安全感﹐他們不是因為害怕孤獨才尋找愛情。他們不是那種在大水中亂抓浮木的人﹐他們有泳技﹐所以也比較不容易把漂過來的稻草當成救命恩人。rn跟沒有安全感的人談戀愛﹐也絕非享受。久而久之﹐壓力真的很大。愛情若“任重而道遠” ﹐你只覺越來越吃力﹐像匹馱著棉花行入溪流中的騾子。rn沒錯﹐我們在愛上一個人的時候﹐我們常覺得自己的生命和他人有了牽連﹐因為煥發光彩﹐我們有了“生命因而又意義” 的感覺﹐沉溺在心有所屬的幸福。rn可是﹐如果我們還沒成熟到能夠享受孤獨﹐那麼﹐這樣的愛情也不過是種幻覺。如果我們能把孤獨視為生命不可剝離的一部分﹐則有助于我們了解戀愛﹐也能減輕失戀時“世界毀滅” 的痛苦。rn無論是誰﹐我想﹐我們都需要孤獨。我們都需依靠孤獨讓自己的混亂沉澱﹑過濾﹐需要孤獨思索﹐只有在孤獨中﹐才聽得見自己的聲音。rn孤獨也是愛的一部分。當他說“我得好好靜一下” 的時候﹐並不意味著﹐你得誠惶誠恐﹐害怕失去他的愛。成熟的愛人尊重愛人的孤獨。rn 论坛

socket 问题,越搞越不明白了。

09-28

刚学网络编程,有些不明白,多多指教 。rnrn// 自定义的消息处理函数rnvoid CSimpleServerDlg::OnSocket(WPARAM wParam, LPARAM lParam)rnrn int error =0;rn int ret =0;rn SOCKET socket rn //SOCKET *socket = new SOCKET;rn if (WSAGETSELECTERROR(lParam))rn rn closesocket(wParam);rn return;rn rn switch(WSAGETSELECTEVENT(lParam))rn rn case FD_ACCEPT:rn //SOCKET * socket = new SOCKET; rn socket = accept(wParam, NULL, NULL);rn if(socket == SOCKET_ERROR)rn rn TRACE("Accept Error: %d \n", (error = WSAGetLastError()));rn //delete socket;rn return;rn rn WSAAsyncSelect(socket,this->GetSafeHwnd(),WM_SOCKET,FD_READ|FD_WRITE|FD_CLOSE);rn break;rn case FD_READ:rn char buff[256];rn ret = recv(wParam, buff, 256, 0);rn if (ret == 0 || ret == SOCKET_ERROR )rn rn TRACE("Recv data error: %d\n", WSAGetLastError());rn closesocket(wParam);rn return;rn rn ............rnrn case FD_WRITE:rn .............rnrn rnrnrn问题1: 函数的开头有 SOCKET socket ,意思是当有客户端请求连接时,用这个套接字和客户端连接,但是他是个局部变量,就算他和客户端连接上了,这个函数执行完毕后,这个套接字就释放了,那么和客户端的连接应该也就不存在了。换句话说,应该定义为指针才是对的。但是这段代码确能很好的和客户端收发消息。是什么原因?rn问题2: 在函数的开头 new 一个套接字,这段代码编译能通过,但是在" case FD_ACCEPT "处 new 一个套接字就报错:initialization of 'socket' is skipped by 'case' label ,是怎么回事?rn问题3:在使用wParam时为什么没有进行转换,比如socket = accept(*(SOCKET*)wParam, NULL, NULL);rn是它自已作为隐式转换,还是其它的原因?rnrn请多多帮忙!!!!!!!!!!!!!rnrn 论坛

没有更多推荐了,返回首页