软件开发: 07 2010存档
打印出什么?这个简单,了解c++的都能答对:打出 "Small Pig“,因为是虚函数嘛。
好,现在改改Pig类:
再运行,就不是打出3行“Small Pig”了,而是:
Pig
Small Pig
Pig
为什么?因为构造函数和析构函数特殊,在它们里面this指针只能当成自己用(而不是当成子类),所以调用虚函数的结果是调用了父类的实现。
这个问题造成了今天的bug,花了不少时间。其实这个注意事项在《Effective c++》里是有的,我也看过,但是....开发中谁还记得那么多条条框框?还是实际犯错印象比较深刻。
有人问了:如果我把call改成纯虚函数会怎样呢?更郁闷,g++编译的时候就会提示构造函数里的call“找不到实现”!
我们项目中有一个daemon,功能是转发并处理消息,为了能看到daemon运行的细节,我们还做了一个monitor线程,由该线程通过某个端口提供简单的web服务,这样就可以直接用浏览器查看daemon的运行状态(比如处理了多少消息,丢弃了多少等)。后来,monitor线程出现了一个bug,造成线程挂掉——于是造成了整个daemon挂掉。这下郁闷了,daemon本身是很重要的,而monitor是不那么重要的,现在是次要部分的bug拖累了重要部分的运行。
这就是多线程程序的缺陷。如果多个线程做的是同样的事情,那还尚可;但如果多个线程,有的做这件事,有的做那件事,而且事情的重要程度不同,那不重要的线程由于代码错误或其他原因死了,其它的线程——包括执行重要功能的——也只能跟着挂。这在健壮性上肯定是不好的。apache采用多进程应该也是出于这样的考虑,因为它的module可能是用户自己写的,可能并不稳定,但由于module不稳定而挂掉整个apache,显然不应该。当然,apache2开始支持多线程,但即使这样,它默认还是多进程的,并没有整个倒向多线程。
也许有人会说:你代码写好一点,不要有bug,多线程不就没事了吗?首先,我们讨论的是软件健壮性的问题——怎样在坏了一部分以后其它部分还能工作,而不是软件正确性的问题——怎样写正确的代码。不是一个方向的问题,并不矛盾。其次,软件不可能没有bug,我们如果能把不同杀伤性的bug通过不同进程把它们隔开,就能降低影响,这跟挖掘bug的目标是一致的——都是为了增加软件的可用性。
所以,多线程并非万金油。为了健壮性,可以考虑把不同性质的任务分到不同的进程上,再由父进程统一管理。而在这些进程之下,可以再有多线程。当然,这样开发就复杂了。