qileilove

blog已经转移至github,大家请访问 http://qaseven.github.io/

IT项目风险管理案例和应对之道

IT项目风险管理案例和应对之道


  IT项目管理从某个意义上来说,就是风险管理。从理论上讲风险管理可以分为三个部分:风险识别、风险分析和风险解决。 传统的风险管理系统只能帮我们较正规地统计和管理风险,这些系统本身是不能规避或解决任何风险的。在实际操作上,由于可能发生风险的种类很多,处理起来所耗费的人力物力也相当可观。在下列的案例中,我们建议的不是一套昂贵而且全面的风险管理系统,而是一套扼住最关键部位,高效且低成本,适合于千万中小企业的小型解决方案。

  一个案例

  在2009年某家在北京海淀区的嵌入式产品公司跟我们讨论项目管理时,该公司的王总监跟我们做了以下沟通。他们项目风险种类可以概略分为四类:

  (1)需求风险 ——对需求理解不够透彻或需求变更频繁;

  (2)人员风险 ——人员生病或离职,一时无法找到替代者;

  (3)技术风险 ——某个关键的技术问题无法快速攻克;

  (4)管理风险 ——管理人员协调能力和执行力能力不足,计划偏差,流程更改和沟通不良等。

  这些风险的发生导致的结果就是项目延期和成本大幅攀升。无法有效处理这些风险的两个最大问题在于:

  (1)对风险的反应迟钝 ——常常是太晚发现问题,以至于无法弥补或是弥补成本太大;

  (2)对过程难以掌控 ——虽已有既定的流程,但由于人员变动、流程变动、系统出错等问题,很难照着走。

  为了让我们更理解,王总监很生动的解释了以上两个问题。对风险反应迟钝的问题,他说,在做项目计划时为求实际,总会多估个20%到40%的时间。如果项目需求清晰,或是团队做过类似项目,就用20%或多些;如果是新项目,或风险因素多便用30%到40%。所以,当某些风险(如,需求变更或人员变动)发生了,一般也未必马上就造成项目延期。可是,如果风险发生量继续增加,或是某一两个风险产生较严重的冲击,在某个时刻就会过了临界点。难的地方就是项目大人员多,就是连项目经理也是见树不见林。

  对过程难以掌控的问题,王总监举了个例子。公司的研发制度里规定,为保证需求的准确性,一个需求的变更要经过(1)该项目经理,(2)一位资深程序员,和(3)该产品经理,等三个关键人审核后才可以进行更改。王总监说:需求变更的过程在逻辑上看似简单,但在实际操作时却不断地发生问题。举例来说,内部沟通主要是以邮件通知的方式进行,需求变更的文档寄来寄去,版本很多,而且邮件总是遗失。另有一次更严重,产品经理因为休假,没能及时查邮件。在等了两天后,因怕误了工期,项目经理便越权要求程序员把代码写了。不巧,产品经理对这需求的更改有他强烈的意见。当他看到在没得到他的同意下就把代码写了,火冒三丈,直接在会议中就和项目经理吵了起来。

  两个控制风险的原则

  虽然风险总是发生,但就如同大多数的公司一样,该公司也不愿意花费太多的精力和时间在这风险管控上,所以在寻求一个低成本又高效的管控方法。王总监和我们在研讨后,使用工具DevSuite基于下列两个原则做了处理。(为避免篇幅太大,以下我们仅把最精彩的点列出来。)

  (1)提高项目的可视性

  不论是哪一种风险,其最后冲击的基本上就是项目本身,延期是最常见的结果。如果是对可能发生的风险都一一进行管控,成本必然很高,而且还可能有疏漏。使用燃尽图(Burn Down Chart)可能是针对项目延期最有效的解决办法,因为它很大程度地提高了项目的可视性。在实际操作时,我们让团队成员每天对其参与的每一任务都键入下列两项数字:1)该任务花费时间,和2)该任务所剩时间。结果就会生了类似如下的燃尽图。

  如图所示,起初这项目被估计是要3480小时完成。大致上来说,一般的研发团队因着人员请假、会议和其他突发事情,平均每人每天只能有六小时花在实际项目工作上。现这项目有七个人参与,估算出来大约需要四个月完成。(也就是从2009年11月29日到2010年3月29日,图中红色直立线为起始线,蓝色直立线为终止线。)这图里共有三条曲线。红色号码?/span>表示到现在为止在该项目的总花费时间,红色号码?表示估算的项目剩余时间,红色号码?是到目前为止所花的时间与剩余时间之和的曲线。到了2010年3月21日就得到了上面的这个图。到了这一天,我们发现原本估计的3480总小时数是低估了,更可能的是?所示的3740小时。

  以纵轴的性质区分,燃尽图可以分为两大类,即纵轴可以是时间或是事件。以范围区分,燃尽图至少可以分为三类:项目级的、任务级的和需求级的。透过燃尽图,我们可以看到项目进行的情况,项目需求是否按计划进入开发流程,工作是否有延时,或者工作安排的饱和度是否适合。如上图所示,我们可以轻易地看到,照着现在的进度,这项目最可能会延期6到7工作天。当高层看到这图时,就可以在资源上做调动,以避免延期产生的不良后果。

  我们刻意使用了这个较理想的图做讲解,为的是让读者更容易理解。它不是个典型的图。在大多情况,使用燃尽图会是比较复杂的,因为它可能包含了需求搜集、编程、单元测试、集成测试和缺陷修复,并且还可能有迭代。所以估算时间会较困难。这个燃尽图的过程是比较稳定的,结果是比较理想的,其原因至少有二:(一)项目里单纯地只有编程、单元测试和缺陷修复任务,全都由程序员搞定;它里头没有需求搜集、集成测试或其他任务。(二)这个项目复杂度低,约一半的编码任务是机械性的,所以估出来的时间较为准确。

  (2)固化工作流以管控过程

  对于公司里既定的流程,我们在DevSuite里以图形化的工作流将其固化。下图就是以前面王总监提到的需求变更流程设计出来的。

  这工作流指明了,在一个变更事件被创建后,它需要经过一个《评审》状态。在评审阶段里,有三个人(A,B和C)要全部同意,才能到达《通过》状态。有任何一人不同意,状态就转到《拒绝》。当一到达《评审》状态,系统马上促发邮件和手机通知,将信息寄给A,B和C。系统可以预先设定这三人有两天的时间评审该变更。假如两天过了,状态仍为《评审》,那就是有人未及时处理该事件。这时候,系统会自动将事件升级,把状态转换为《升级处理》,系统马上促发邮件,将信息寄给研发部王总监。王总监可以斟酌情况,做最妥善的处理。

  使用固化的工作流至少有四个优点:1)提高通知效率 ?邮件和手机自动通知提高效率,沟通出错的机会减少了;2)避免流程出错 ?DevSuite的工作流将流程完全自动化,每个人在收到邮件或短信通知时,照着工作流中既定的步骤操作就行了,省心又省力; 3)工作流变动时处理很容易 ?当流程或人员变动时,系统配置员可以轻易地花几分钟就做完调整,之后所有团队成员就照着流程走便行了;4)避免摩擦?人是有情绪的,固化的工作流使得操作完全对事不对人,避免了人和人之间不必要的摩擦。

  以上提到的软件项目风险实例几乎在每个项目中都出现,而且,它们造成的损失也是严重的。所幸,从实际操作中,我们发现处理它们的成本并不高:1)培训团队成员照工作流中既定的步骤操作,学会填写任务花费时间和任务所剩时间,并理解意图,所花时间不超过1小时,2)系统配置员要了解需求,设计工作流,并设置人员(如例子中的A、B、C和走流程的人)的权限等,所花费时间在1到3天之间,也算合理,3)以往当团队人员或评审流程有变动,管理人员要更改文档并向所有人宣布;现系统配置员只要花几分钟改系统配置,一切就就绪了。

  小结

  这并不是一个全方位的风险管控系统;相反的,它是个相当简化,只对关键点作处理的系统。虽然只是做在关键点上,但效果却十分明显。就拿需求变更来说,需求变更一直都是项目中让人恨得牙痒痒的瘤。既然需求变更是不可避免,那我们所能做的就是,尽可能减少变更的次数,降低变更造成的冲击。以往大多数人审核需求变更时较为草率,导致同一个功能点变了又变。在一轮又一轮的返工后,程序员和测试人员会产生倦怠感,编码和测试的质量一再下降。使用了DevSuite后,所有的操作都在系统里留下记录,这统计在年终时可以作为考评的参考材料。自然而然地,审核人员就很严谨地审核每一个需求变更。而且,因为系统设置了每人只有两天的时间处理,审核人员处理需求变更时不仅是快,而且较仔细。单单就这个变化,就使得整个团队的气象焕然一新。

  在系统实施后半年,我们做了客户回访,我劈头就问王总监,说的那位产品经理还跟项目经理还吵架吗?瞪了我一眼,停了一下,然后皱了皱眉头说:倒是不吵架了。他们俩现在成了好朋友,联合起来一起对付我了。他自己呵呵呵地笑了起来

posted @ 2011-12-08 14:57 顺其自然EVO 阅读(175) | 评论 (0)编辑 收藏

如何有效实现软件的需求管理(5)

需求分析阶段完了以后,就是需求设计,然后就是需求实现了,过程看起来很简单,但是实际工作不简单,上面谈到的需求管理的几点严格要求一直贯穿着整个过程的始末。

  接下来我会结合我们公司实际的流程来介绍一下需求管理的实际实现。

  如果看过我之前的文章,应该知道我们公司的背景,我们公司也是做软件开发的,所以对于需求管理这块也是相当重视的。我们是用敏捷的模式来管理整个软件开发的,所以需求管理的阶段也是符合敏捷的模式的,但是对于需求管理的几点严格要求还是基本上也是遵守的。

  我们公司是用 TechExcel 的需求管理工具 DevSpec 来管理整个需求过程的,其实我们是买了他们的整套软件生命周期管理的解决方案,名称叫做DevSuite,而DevSpec 是其中一个工具,能与DevSuite 解决方案的其他工具无缝集成,帮助共同管理开发、测试、计划等阶段。

  在DevSpec中,对于需求的管理是通过条目化的方式来管理的,所谓的条目化就是说一个需求就是一个条目,这个条目既包括了对这个需求的描述,还包括了对这个需求的处理过程的跟踪:

  对于需求的描述而言,DevSpec是通过属性字段的方式实现的,你可以用字段来尽可能真实描述需求,其中包括标题,状态,负责人,描述,时间,附件等基本字段,当然你还可以大量自定义属性字段和页面来帮助更好地描述这个需求。

  对于需求的处理过程而言,

  我们知道需求的处理是要有流程的,简单的就是从需求分析-->需求设计-->需求实现,复杂点的还需要加上审核,就像我上面给大家看过的那个流程图一样, 我这里再贴一下

  不过光有流程其实没用,我相信任何公司的需求处理都会有流程,只是严格不严格,认真不认真的区别罢了,不遵照流程处理的需求有非常高的可能性不成功,所以为了解决这个问题,DevSpec 中专门设计可自定义的工作流程,你可以自己定义需求需要经过哪些流程才能进入开发,而一旦流程定义完成以后,需求的处理就会被强制按照流程的进行,你自己想马虎马虎,松懈松懈都没办法做到。

  在流程中,DevSpec可以给每个过程设置不同负责人和权限,比如分析这个过程是小王处理,所以只有小王才能看到这个需求并且处理这个需求,其他人如果没有权限就看不到这个需求;而小王处理完他的工作后,他不一定有权力把这个需求转到下一个过程,因为需要另外一个人审核以后才能继续下去。

  这样子的话,

  第一,你的处理流程会很清晰,这一步处理完了,下一步是什么,一目了然;

  第二,你的管理流程也很透明,现在谁处理,接下来该谁处理,清清楚楚。

  第三,你的权力流程也很明了,什么级别的人能做什么事情都可以设置,哪些该做的不该做的,哪些该看不该看的,都很简单就可以设置,避免了一些人看到不该看的内容,做了不该做的事情。

  有了这些属性和流程,我们就可以正式开始 DevSpec 的需求管理了.

  (未完待续)

posted @ 2011-12-08 14:50 顺其自然EVO 阅读(315) | 评论 (0)编辑 收藏

如何进行高效JavaScript单元测试

     摘要: 如何进行高效JavaScript单元测试 导读:能在一个浏览器上运行的JavaScript并不一定能在其他浏览器上运行。如果没有对代码进行单元测试,那么在决定升级或支持新浏览器的时候,组织就需要花钱测试或重新测试Web应用程序。在本文中,了解JavaScript单元测试如何帮助您降低测试成本,轻松支持更多浏览器。  一个损坏的JavaScript代码示例  Web应用程序面临的一个最大挑战是支持不...  阅读全文

posted @ 2011-12-08 14:47 顺其自然EVO 阅读(205) | 评论 (0)编辑 收藏

小谈软件测试的健忘症

测试界,有两种非常极端的情况:一种是过早充分地测试,包括过早地介入测试,文档,测试计划,用例以及脚本等准备的非常充分;一种是随机测试,无文档无计划,完全靠测试人员的个人能力。这两种情况都会出现测试的健忘症。

  首先,第一种情况会出现什么问题呢?刚刚做好的测试计划或者测试用例又要重新制定和编写,原因是介入太早需求还没有完全确定下来,反复的工作浪费很多的人力和时间。而且在如今互联网需求多变的情况下,如此充分的工作说不好在2个月后的重构中又要重新开始,这时候,健忘症体现在:

  1、之前花了很多时间设计的测试用例又要被遗弃或者做大量修改,用例的生命周期很短,测试用例并不是解决健忘症的最好方法,当然,对于一个新涉入此功能的测试人员可以通过用例了解到大概的测试点,快速熟悉。

  2、看过一本书上介绍的杀虫剂悖论:当你向一块农田喷药时,将杀死很多的虫子,但是存活下来的抵抗性将增强。用在软件测试上也不例外,之前所有的用例执行过后能发现的缺陷已经全部暴露,之后的回归测试只是为了确保原来的功能正常,而原本未被暴露的缺陷已经具有抵抗力了。大家已经忘记当时使用的什么测试技巧,而现在又该使用什么别的技巧来找出这些隐藏很深的bug呢?

  其次,对于第二种情况,经常出现的事情是,测试的内容不够明确,对于已经测试的部分和未测试的部分都不能明确标记,甚者不知道发现的bug如何复现,为了再次重现此bug都需要时间,这就是最大的健忘症,毫无准备的测试工作,不能保证测试的质量。

  如何避免呢?首先要折中一下这两种情况,对于项目生命周期很长的,可以较为充分地开展测试工作,准备充分的文档以及用例,除此之外,需要文档化的就是测试策略和测试规范。对于生命周期不长需求多变的项目,如web项目,除了有计划的开展工作外,还需要把测试策略和技巧记录下来,并且标记哪种最有效,发现的bug最多,哪种效果稍差,这将有利于日后策略的调整。不同的测试过程采用不同的测试策略,以发现更多的缺陷,同时可以引入交叉测试和探索性测试,来找出隐藏很深的缺陷。而且这些策略可以被日后类似项目复用,解决了测试的健忘症。

posted @ 2011-12-08 14:33 顺其自然EVO 阅读(145) | 评论 (0)编辑 收藏

小谈软件测试的健忘症

测试界,有两种非常极端的情况:一种是过早充分地测试,包括过早地介入测试,文档,测试计划,用例以及脚本等准备的非常充分;一种是随机测试,无文档无计划,完全靠测试人员的个人能力。这两种情况都会出现测试的健忘症。

  首先,第一种情况会出现什么问题呢?刚刚做好的测试计划或者测试用例又要重新制定和编写,原因是介入太早需求还没有完全确定下来,反复的工作浪费很多的人力和时间。而且在如今互联网需求多变的情况下,如此充分的工作说不好在2个月后的重构中又要重新开始,这时候,健忘症体现在:

  1、之前花了很多时间设计的测试用例又要被遗弃或者做大量修改,用例的生命周期很短,测试用例并不是解决健忘症的最好方法,当然,对于一个新涉入此功能的测试人员可以通过用例了解到大概的测试点,快速熟悉。

  2、看过一本书上介绍的杀虫剂悖论:当你向一块农田喷药时,将杀死很多的虫子,但是存活下来的抵抗性将增强。用在软件测试上也不例外,之前所有的用例执行过后能发现的缺陷已经全部暴露,之后的回归测试只是为了确保原来的功能正常,而原本未被暴露的缺陷已经具有抵抗力了。大家已经忘记当时使用的什么测试技巧,而现在又该使用什么别的技巧来找出这些隐藏很深的bug呢?

  其次,对于第二种情况,经常出现的事情是,测试的内容不够明确,对于已经测试的部分和未测试的部分都不能明确标记,甚者不知道发现的bug如何复现,为了再次重现此bug都需要时间,这就是最大的健忘症,毫无准备的测试工作,不能保证测试的质量。

  如何避免呢?首先要折中一下这两种情况,对于项目生命周期很长的,可以较为充分地开展测试工作,准备充分的文档以及用例,除此之外,需要文档化的就是测试策略和测试规范。对于生命周期不长需求多变的项目,如web项目,除了有计划的开展工作外,还需要把测试策略和技巧记录下来,并且标记哪种最有效,发现的bug最多,哪种效果稍差,这将有利于日后策略的调整。不同的测试过程采用不同的测试策略,以发现更多的缺陷,同时可以引入交叉测试和探索性测试,来找出隐藏很深的缺陷。而且这些策略可以被日后类似项目复用,解决了测试的健忘症。

posted @ 2011-12-08 14:33 顺其自然EVO 阅读(123) | 评论 (0)编辑 收藏

优化Linux下的内核TCP参数以提高系统性能

 内核的优化跟服务器的优化一样,应本着稳定安全的原则。下面以64位的Centos5.5下的Squid服务器为例来说明,待客户端与服务器端建立TCP/IP连接后就会关闭SOCKET,服务器端连接的端口状态也就变为TIME_WAIT了。那是不是所有执行主动关闭的SOCKET都会进入TIME_WAIT状态呢?有没有什么情况使主动关闭的SOCKET直接进入CLOSED状态呢?答案是主动关闭的一方在发送最后一个ACK后就会进入TIME_WAIT状态,并停留2MSL(Max Segment LifeTime)时间,这个是TCP/IP必不可少的,也就是“解决”不了的。

  TCP/IP的设计者如此设计,主要原因有两个:

  防止上一次连接中的包迷路后重新出现,影响新的连接(经过2MSL时间后,上一次连接中所有重复的包都会消失)。

  为了可靠地关闭TCP连接。主动关闭方发送的最后一个ACK(FIN)有可能会丢失,如果丢失,被动方会重新发FIN,这时如果主动方处于CLOSED状态,就会响应RST而不是ACK。所以主动方要处于TIME_WAIT状态,而不能是CLOSED状态。另外,TIME_WAIT并不会占用很大的资源,除非受到攻击。

  在Squid服务器中可输入查看当前连接统计数的命令,如下所示:

  • netstat -n| awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'  
  • LAST_ACK 14  
  • SYN_RECV 348  
  • ESTABLISHED 70  
  • FIN_WAIT1 229  
  • FIN_WAIT2 30  
  • CLOSING 33  
  • TIME_WAIT 18122
  •   CLOSED:无连接是活动的或正在进行中的。

      LISTEN:服务器在等待进入呼叫。

      SYN_RECV:一个连接请求已经到达,等待确认。

      SYN_SENT:应用已经开始,打开一个连接。

      ESTABLISHED:正常数据传输状态。

      FIN_WAIT1:应用说它已经完成。

      FIN_WAIT2:另一边已同意释放。

      CLOSING:两边同时尝试关闭。

      TIME_WAIT:另一边已初始化一个释放。

      LAST_ACK:等待所有分组死掉。

      也就是说,这条命令可以把当前系统的网络连接状态分类汇总。

      在Linux下高并发的Squid服务器中,TCP TIME_WAIT套接字数量经常可达两三万,服务器很容易就会被拖死。不过,我们可以通过修改Linux内核参数来减少Squid服务器的TIME_WAIT套接字数量,命令如下所示:

    vim /etc/sysctl.conf

      然后,增加以下参数:

  • net.ipv4.tcp_fin_timeout = 30 
  • net.ipv4.tcp_keepalive_time = 1200 
  • net.ipv4.tcp_syncookies = 1 
  • net.ipv4.tcp_tw_reuse = 1 
  • net.ipv4.tcp_tw_recycle = 1 
  • net.ipv4.ip_local_port_range = 1024 65000  
  • net.ipv4.tcp_max_syn_backlog = 8192 
  • net.ipv4.tcp_max_tw_buckets = 5000



  • 其中:

      net.ipv4.tcp_syncookies=1表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookie来处理,可防范少量的SYN攻击。默认为0,表示关闭。

      net.ipv4.tcp_tw_reuse=1表示开启重用。允许将TIME-WAIT套接字重新用于新的TCP连接。默认为0,表示关闭。

      net.ipv4.tcp_tw_recycle=1表示开启TCP连接中TIME-WAIT套接字的快速回收。默认为0,表示关闭。

      net.ipv4.tcp_fin_timeout=30表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间。

      net.ipv4.tcp_keepalive_time=1200表示当keepalive启用时,TCP发送keepalive消息的频度。默认是2小时,这里改为20分钟。

      net.ipv4.ip_local_port_range=1024 65000表示向外连接的端口范围。默认值很小:32768~61000,改为1024~65000。

      net.ipv4.tcp_max_syn_backlog=8192表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数。

      net.ipv4.tcp_max_tw_buckets=5000表示系统同时保持TIME_WAIT套接字的最大数量,如果超过这个数字,TIME_WAIT套接字将立刻被清除并打印警告信息。默认为180000,改为5000。对于Apache、Nginx等服务器,前面介绍的几个参数已经可以很好地减少TIME_WAIT套接字数量,但是对于Squid来说,效果却不大。有了此参数就可以控制TIME_WAIT套接字的最大数量,避免Squid服务器被大量的TIME_WAIT套接字拖死。

      执行以下命令使内核配置立即生效:

    /sbin/sysctl -p

      如果是用于Apache或Nginx等的Web服务器,或Nginx的反向代理,则只需要更改以下几项即可:

  • net.ipv4.tcp_syncookies=1 
  • net.ipv4.tcp_tw_reuse=1 
  • net.ipv4.tcp_tw_recycle = 1 
  • net.ipv4.ip_local_port_range = 1024 65000
  •   执行以下命令使内核配置立即生效: /sbin/sysctl -p如果是邮件服务器,则建议内核方案如下:

  • net.ipv4.tcp_fin_timeout = 30 
  • net.ipv4.tcp_keepalive_time = 300 
  • net.ipv4.tcp_tw_reuse = 1 
  • net.ipv4.tcp_tw_recycle = 1 
  • net.ipv4.ip_local_port_range = 5000 65000  
  • kernel.shmmax = 134217728
  •   执行以下命令使内核配置立即生效: /sbin/sysctl -p当然这些都只是最基本的更改,大家还可以根据自己的需求来更改内核的设置,同样也要本着稳定的原则,如果服务器不稳定的话,一切工作和努力都会白费。如果以上优化仍无法满足你的要求,有可能你需要定制你的服务器内核或升级服务器硬件。至于服务的配置优化,超出了本章的内容,大家可根据自己的需求有针对性地进行更改。

    posted @ 2011-12-08 14:30 顺其自然EVO 阅读(1142) | 评论 (0)编辑 收藏

    轻松解决SQL Server 2005中的常见问题

    问题1:使用.net2005自带的SQL-Express连接不上。

      解决方法:

      1、网络防火墙阻止数据库连接;

      2、默认SQL-Express没有启动Sa账户->下载一个management studio express界面工具管理SQL-Express

      3、无线网络会出现根据机器名找不到SQL服务器的情况,直接用IP连接

      4、服务端通过开始菜单打开->配置工具->SQL Server外围应用配置器->服务和连接的外围应用配置器->远程连接->右边选择“本地连接和远程连接”->同时使用TCP/IP和named pipes.

      问题2:在Win-XP上安装开发版提示“对性能监视器计数器注册表执行系统配置检查失败”

      解决方法:

      注册表定位到/local_machine/software/microsoft/windows nt/currentversion/perflib下,两个值last counter 和last help 的值改成和004(英文系统为009)目录中相关键值的最大值一样。

      问题3:其他版本的SQL2005数据库通过“复制”、“导出”、“备份”等方法将数据库复制到SQL DEV上面去后,右键表、新建表等会出现以下错误:

      类别不支持集合(或类别对象为远程对象) (异常来自 HRESULT:0x80040110 (CLASS_E_NOAGGREGATION)) (Microsoft.SqlServer.SqlTools.VSIntegration)

      分析:可能是SQL Server 2005的一个Bug,也可能是.net framework变化了,比如安装了其他版本的SQL Server 2005。

      解决方法:

      经验证,这样操作先卸载SQL DEV(网上说是卸载客户端即可,笔者是把所有的SQL Server 2005都删掉),再重装/修复.NET 2.0 Framework,再重装SQL DEV,解决问题。

      问题4:vs2005中gridview不能删除SQL2005中VARCHAR类型字段,提示--“异常详细信息: System.Data.SqlClient.SqlException: 数据类型 ntext 和 nvarchar 在 equal to 运算符中不兼容。”

      解决方法:

      SqlDataSource连接的时候不能选择并发控制,就可以编辑和删除了,否则即使不报错,也无法操作。

      注释:在安装SQL Server 2005的过程中需要关闭注册表监视软件和病毒防护等软件。

    posted @ 2011-12-08 14:29 顺其自然EVO 阅读(355) | 评论 (0)编辑 收藏

    多线程的那点儿事(基础篇)

    多线程编程是现代软件技术中很重要的一个环节。要弄懂多线程,这就要牵涉到多进程?当然,要了解到多进程,就要涉及到操作系统。不过大家也不要紧张,听我慢慢道来。这其中的环节其实并不复杂。

      (1)单CPU下的多线程

      在没有出现多核CPU之前,我们的计算资源是唯一的。如果系统中有多个任务要处理的话,那么就需要按照某种规则依次调度这些任务进行处理。什么规则呢?可以是一些简单的调度方法,比如说

      1)按照优先级调度

      2)按照FIFO调度

      3)按照时间片调度等等

      当然,除了CPU资源之外,系统中还有一些其他的资源需要共享,比如说内存、文件、端口、socket等。既然前面说到系统中的资源是有限的,那么获取这些资源的最小单元体是什么呢,其实就是进程。

      举个例子来说,在linux上面每一个享有资源的个体称为task_struct,实际上和我们说的进程是一样的。我们可以看看task_struct(linux 0.11代码)都包括哪些内容

  • struct task_struct {  
  • /* these are hardcoded - don't touch */  
  •     long state; /* -1 unrunnable, 0 runnable, >0 stopped */  
  •     long counter;  
  •     long priority;  
  •     long signal;  
  •     struct sigaction sigaction[32];  
  •     long blocked;   /* bitmap of masked signals */  
  • /* various fields */  
  •     int exit_code;  
  •     unsigned long start_code,end_code,end_data,brk,start_stack;  
  •     long pid,father,pgrp,session,leader;  
  •     unsigned short uid,euid,suid;  
  •     unsigned short gid,egid,sgid;  
  •     long alarm;  
  •     long utime,stime,cutime,cstime,start_time;  
  •     unsigned short used_math;  
  • /* file system info */  
  •     int tty;        /* -1 if no tty, so it must be signed */  
  •     unsigned short umask;  
  •     struct m_inode * pwd;  
  •     struct m_inode * root;  
  •     struct m_inode * executable;  
  •     unsigned long close_on_exec;  
  •     struct file * filp[NR_OPEN];  
  • /* ldt for this task 0 - zero 1 - cs 2 - ds&ss */  
  •     struct desc_struct ldt[3];  
  • /* tss for this task */  
  •     struct tss_struct tss;  
  • };
  •   每一个task都有自己的pid,在系统中资源的分配都是按照pid进行处理的。这也就说明,进程确实是资源分配的主体。

      这时候,可能有朋友会问了,既然task_struct是资源分配的主体,那为什么又出来thread?为什么系统调度的时候是按照thread调度,而不是按照进程调度呢?原因其实很简单,进程之间的数据沟通非常麻烦,因为我们之所以把这些进程分开,不正是希望它们之间不要相互影响嘛。

      假设是两个进程之间数据传输,那么需要如果需要对共享数据进行访问需要哪些步骤呢

      1)创建共享内存

      2)访问共享内存->系统调用->读取数据

      3)写入共享内存->系统调用->写入数据

     要是写个代码,大家可能就更明白了

  • #include <unistd.h>   
  • #include <stdio.h>   
  •   
  • int value = 10;  
  •   
  • int main(int argc, char* argv[])  
  • {  
  •     int pid = fork();  
  •     if(!pid){  
  •         Value = 12;  
  •         return 0;  
  •     }  
  •     printf("value = %d\n", value);  
  •     return 1;  
  • }
  •   上面的代码是一个创建子进程的代码,我们发现打印的value数值还是10。尽管中间创建了子进程,修改了value的数值,但是我们发现打印下来的数值并没有发生改变,这就说明了不同的进程之间内存上是不共享的。

      那么,如果修改成thread有什么好处呢?其实最大的好处就是每个thread除了享受单独cpu调度的机会,还能共享每个进程下的所有资源。要是调度的单位是进程,那么每个进程只能干一件事情,但是进程之间是需要相互交互数据的,而进程之间的数据都需要系统调用才能应用,这在无形之中就降低了数据的处理效率。

      (2)多核CPU下的多线程

      没有出现多核之前,我们的CPU实际上是按照某种规则对线程依次进行调度的。在某一个特定的时刻,CPU执行的还是某一个特定的线程。然而,现在有了多核CPU,一切变得不一样了,因为在某一时刻很有可能确实是n个任务在n个核上运行。我们可以编写一个简单的open mp测试一下,如果还是一个核,运行的时间就应该是一样的。

  • #include <omp.h>   
  • #define MAX_VALUE 10000000   
  •   
  • double _test(int value)  
  • {  
  •     int index;  
  •     double result;  
  •   
  •     result = 0.0;  
  •     for(index = value + 1; index < MAX_VALUE; index +=2 )  
  •         result += 1.0 / index;  
  •   
  •     return result;  
  • }  
  •   
  • void test()  
  • {  
  •     int index;  
  •     int time1;  
  •     int time2;  
  •     double value1,value2;  
  •     double result[2];  
  •   
  •     time1 = 0;  
  •     time2 = 0;  
  •   
  •     value1 = 0.0;  
  •     time1 = GetTickCount();  
  •     for(index = 1; index < MAX_VALUE; index ++)  
  •         value1 += 1.0 / index;  
  •   
  •     time1 = GetTickCount() - time1;  
  •   
  •     value2 = 0.0;  
  •     memset(result , 0, sizeof(double) * 2);  
  •     time2 = GetTickCount();  
  •   
  • #pragma omp parallel for   
  •     for(index = 0; index < 2; index++)  
  •         result[index] = _test(index);  
  •   
  •     value2 = result[0] + result[1];  
  •     time2 = GetTickCount() - time2;  
  •   
  •     printf("time1 = %d,time2 = %d\n",time1,time2);  
  •     return;  
  • }
  •   (3)多线程编程

      为什么要多线程编程呢?这其中的原因很多,我们可以举例解决

      1)有的是为了提高运行的速度,比如多核cpu下的多线程

      2)有的是为了提高资源的利用率,比如在网络环境下下载资源时,时延常常很高,我们可以通过不同的thread从不同的地方获取资源,这样可以提高效率

      3)有的为了提供更好的服务,比如说是服务器

      4)其他需要多线程编程的地方等等

    posted @ 2011-12-08 14:28 顺其自然EVO 阅读(172) | 评论 (0)编辑 收藏

    Java七步创建以JDBC连接数据库的程序

    Java七步创建以JDBC连接数据库的程序

     JDBC连接数据库

      ◆ 创建一个以JDBC连接数据库的程序,包含7个步骤:

      1、加载JDBC驱动程序:

      在连接数据库之前,首先要加载想要连接的数据库的驱动到JVM(Java虚拟机), 这通过java.lang.Class类的静态方法forName(String className)实现。

      例如:

  • try
  • //加载MySql的驱动类 
  • Class.forName("com.mysql.jdbc.Driver") ; 
  • }catch(ClassNotFoundException e){ 
  • System.out.println("找不到驱动程序类 ,加载驱动失败!"); 
  • e.printStackTrace() ; 
  • }
  •   成功加载后,会将Driver类的实例注册到DriverManager类中。

      2、提供JDBC连接的URL

      ◆ 连接URL定义了连接数据库时的协议、子协议、数据源标识。

      ◆ 书写形式:协议:子协议:数据源标识

      协议:在JDBC中总是以jdbc开始

      子协议:是桥连接的驱动程序或是数据库管理系统名称。

      数据源标识:标记找到数据库来源的地址与连接端口。

      例如:(MySql的连接URL)

  • jdbc:mysql: 
  • //localhost:3306/test?useUnicode=true&characterEncoding=gbk ;
  •   useUnicode=true:表示使用Unicode字符集。如果characterEncoding设置为gb2312或GBK,本参数必须设置为true 。characterEncoding=gbk:字符编码方式。

      3、创建数据库的连接

      ◆ 要连接数据库,需要向java.sql.DriverManager请求并获得Connection对象,该对象就代表一个数据库的连接。

      ◆ 使用DriverManager的getConnectin(String url,String username,String password)方法传入指定的欲连接的数据库的路径、数据库的用户名和 密码来获得。

      例如:

  • //连接MySql数据库,用户名和密码都是root 
  • String url = "jdbc:mysql://localhost:3306/test" ; 
  • String username = "root" ; 
  • String password = "root" ; 
  • try
  • Connection con = 
  • DriverManager.getConnection(url , username , password ) ; 
  • }catch(SQLException se){ 
  • System.out.println("数据库连接失败!"); 
  • se.printStackTrace() ; 
  • }
  •   4、创建一个Statement

      ◆ 要执行SQL语句,必须获得java.sql.Statement实例,Statement实例分为以下3种类型:

      1、执行静态SQL语句。通常通过Statement实例实现。

      2、执行动态SQL语句。通常通过PreparedStatement实例实现。

      3、执行数据库存储过程。通常通过CallableStatement实例实现。

      具体的实现方式:

  • Statement stmt = con.createStatement() ; 
  • PreparedStatement pstmt = con.prepareStatement(sql) ; 
  • CallableStatement cstmt = 
  • con.prepareCall("{CALL demoSp(? , ?)}") ;
  • \\\5、执行SQL语句

      Statement接口提供了三种执行SQL语句的方法:executeQuery 、executeUpdate和execute

      1、ResultSet executeQuery(String sqlString):执行查询数据库的SQL语句,返回一个结果集(ResultSet)对象。

      2、int executeUpdate(String sqlString):用于执行INSERT、UPDATE或DELETE语句以及SQL DDL语句,如:CREATE TABLE和DROP TABLE等

      3、execute(sqlString):用于执行返回多个结果集、多个更新计数或二者组合的语句。

      具体实现的代码:

  • ResultSet rs = stmt.executeQuery("SELECT * FROM ...") ; 
  • int rows = stmt.executeUpdate("INSERT INTO ...") ; 
  • boolean flag = stmt.execute(String sql) ;
  •   6、处理结果

      两种情况:

      1、执行更新返回的是本次操作影响到的记录数。

      2、执行查询返回的结果是一个ResultSet对象。

      ◆ ResultSet包含符合SQL语句中条件的所有行,并且它通过一套get方法提供了对这些

      行中数据的访问。

      ◆ 使用结果集(ResultSet)对象的访问方法获取数据:

  • while(rs.next()){ 
  • String name = rs.getString("name") ; 
  • String pass = rs.getString(1) ; // 此方法比较高效 
  • }
  •   (列是从左到右编号的,并且从列1开始)

      7、关闭JDBC对象

      操作完成以后要把所有使用的JDBC对象全都关闭,以释放JDBC资源,关闭顺序和声明顺序相反:

      1、关闭记录集

      2、关闭声明

      3、关闭连接对象

  • if(rs != null){ // 关闭记录集 
  • try
  • rs.close() ; 
  • }catch(SQLException e){ 
  • e.printStackTrace() ; 
  • if(stmt != null){ // 关闭声明 
  • try
  • stmt.close() ; 
  • }catch(SQLException e){ 
  • e.printStackTrace() ; 
  • if(conn != null){ // 关闭连接对象 
  • try
  • conn.close() ; 
  • }catch(SQLException e){ 
  • e.printStackTrace() ; 
  • }
  • posted @ 2011-12-08 14:20 顺其自然EVO 阅读(272) | 评论 (0)编辑 收藏

    Code Review的方式和流程

    Code Review的方式和流程

    经过在公司1年多的code review的经验回顾:原来有fisheye,开发提交代码后旺旺通知测试工程师,并通过读代码来了解测试范围,并发现代码中的错误。

      后来,最近半年的项目、日常测试过程中都是开发提交代码后,测试和开发一起借用SVN工具等代码版本控制工具,或Eclipse 等IDE进行Code Review。

      这其中的一个转变就是测试由被动接收消息,到主动查看SVN 的log看代码变动。测试工程师的态度由被动变为主动,是个不小的进步。

      目前看来,进行 Code Review 的目的或效果有:

      1、让测试熟悉所测产品的业务代码,提升代码的阅读能力;

      2、提早发现代码里面的bug,低成本保障质量,防患于未然;

      3、提前预知并评估并确认测试范围,减少测试工作量;

      4、促进开发、测试间的沟通、交流和协作。

      功能测试工程师参加code  review提前做的一些准备:

      1、简单的编码规范

      2、Java编程的基本知识

      经过这Code Review的实践,感觉Code Review目前比较适合我们工作的方式是:

    阶段

    Code Review的方式

    准备

    1、了解开发的UC设计,及基本的编码知识;
    2、了解基本的代码的编码规范;
    3、确定code review的范围:业务的核心代码逻辑。

    形式

    项目:会议室+投影仪。日常:在开发/测试的位置上即可。

    参加人员

    PM,PTM,相关开发工程师、测试工程师

    可以采用的方法

    1、编码人员讲解,其他开发、测试人员检查。
    2、代码静态检测工具:Findbug
    3、缺陷检查表,但是这个太正式了,不一定需要。

    注意点

    1、限时:一般不要超过1个小时为宜;如果量大,最好分批review。
    2、不要现场修改代码,发现问题后,测试可以直接在bug管理平台记录。

    产出

    1、Bug记录;
    2、静态分析错误报告;
    3、结果:code review 是否通过。

      比较合适的,并且目前使用的流程是:

    posted @ 2011-12-07 11:34 顺其自然EVO 阅读(792) | 评论 (0)编辑 收藏

    仅列出标题
    共394页: First 上一页 350 351 352 353 354 355 356 357 358 下一页 Last 
    <2024年11月>
    272829303112
    3456789
    10111213141516
    17181920212223
    24252627282930
    1234567

    导航

    统计

    常用链接

    留言簿(55)

    随笔分类

    随笔档案

    文章分类

    文章档案

    搜索

    最新评论

    阅读排行榜

    评论排行榜