2010-02-04 05:15:56
来自: Milo
0 bug的评论



2
(如果不知道在下這超長書評的始末,建議先看 http://www.douban.co m/review/2963028/)
第4-6章書評 http://www.douban.co m/review/2971127/
第7-8章書評 http://www.douban.co m/review/2973532/
第9-12章書評 http://www.douban.co m/review/2975703/
雖然這本書使我有點失望,無可否認,它是獨特的。
第一,作者公開了他的商業用代碼,相信沒有很多國內公司會批准這麼做。這讓同行可以窺探競爭對手的狀況,所謂知己知彼,百戰百勝。
第二,本書描述的範疇,確實是國內比較少見的。從內容來說,個人認為較到題的(但會賣得不好的)書名應該是《C(++)伺服器程序庫開發》,範疇來說接近《C++網絡編程》,但減去網絡、理論及++。
第三,本書能令國內程序員(作者除外)間討論許多技術性話題,也讓我們去獨立思考求證,網友間互相學習。我懷疑是內地 IT 出版史上的一個標誌性刊物。
意見其實比較多,時間關係,以下輕談一些簡單的。
== C/C++ ==
在我第一篇評論裡,因為主要是指出1-3章懷疑錯誤的地方,我沒有提及對本書 C++ 代碼的意見。但我和很多讀者都有一些微言。看畢全書後,個人有以下分析。
面向對象 (Object Oriented) 在書中是重新被定義的 (見 P. 216)。個人認為,本書使用 C++ 的方式,比較接近 Object-based 語言 (http://en.wikipedia. org/wiki/Object-base d_language)──沒有繼承 (inheritance)、沒有多態 (polymorphism),只是把狀態 (state) 和操作 (operations) 結合成為對象,並實行資料封裝(data encapsulation)。
但個人覺得,在這種情況下,又不使用 C++ 的標準庫,單純用 C 更能使程序實現和介面變得簡單,而同時保留了資料封裝。舉例:
// C++ 版本 header
class Mutex
{
public:
Mutex();
~Mutex() throw();
void Lock();
void Unlock();
private:
Mutex(const Mutex&); // 防止 copy construction
Mutex& operator=(const Mutex&); // 防止 assignment
HANDLE m_Mutex;
};
// 使用 C++ 版本
Mutex* m= new Mutex;
m->Lock();
m->Unlock();
delete m;
// ---------------------------------------
// C 版本 header
struct Mutex; // 前置聲明
Mutex* MutexCreate();
void MutexDestroy(Mutex*);
void MutexLock(Mutex*);
void MutexUnlock(Mutex*);
// C 版本 implementation (不用在對外 header 定義)
struct Mutex
{
HANDLE m_Mutex;
};
// Uisng C version
Mutex* m= MutexCreate();
MutexLock(m);
MutexUnlock(m);
MutexDestroy(m);
雖然這種比較不完全等價 (C++ 可以創建非指針的Mutex型別),但可以很清楚看到,C 版本的 API (在 header 裡的函數定義) 比 C++ 還簡潔,而且跨平台部份(那個 struct) 也不會顯露在 header 裡 (這有點不準確,C++ 可用 Pimpl 手法,不過就更複雜了)。
使用時,C 版本的懷處是要在每個函數前加 Mutex 前綴,但這樣可能還增加了可讀性。
我的結論是,這種 object-based 方式用 C 語言比較好。用 C++ 去封裝這樣的 C 的 API 的作用不大,只會更複雜。
書中因為沒有重載 operator new 或使用 replacement new,出現要 C++ 類內部使用 malloc() 形式去分配 struct 成員變量,使代碼更複雜及難閱讀。簡潔的代碼是避免出錯的關鍵。
== 防禦式編程==
書中的代碼,經常用 if() 來檢查一些正確程式不會出現的情況。例如最常見是一些 precondition:
// P. 336
bool CTonyBuffer::SetSize(int nSize)
{
if (!m_pMemPool) return false; // 防禦性設計,如果內存池指針為空,無法工作
// ...
}
在該類裡,m_pMemPool是在建構函數就指定的,之後該對象的生命周期裡不能改變這個變量。所以在正確的程式裡 m_pMemPool == NULL 不應該在運作期出現。出現的話,就是代碼本身有錯誤。
使用 return 去逃避這個錯誤,會使那個 bug 更難被發現 (例如調用者沒有檢查傳回值,又或者調用者檢查到錯誤又繼續逃避這個錯誤)。
比較好的方法是立即終止程序,並提示在那個函式出錯。沒錯,這就是 C 的 assert()。而實際應用時通常會客制化這個函式(實際上是宏)。測試沒發現錯誤的話,可在發行版忽略 assert 去減少 overhead。
減少這種 if 亦等同減少代碼的複雜度和測試難度(一些複雜度 metric 包含計算有多少個條件分支)。
簡單來說,應盡量讓 bug 浮現出來。
== 修改代碼 ==
書中多個章節都強調,盡量不要修改代碼,例如 P. 100 「......任何代碼的修改、替換都可能造成新的不穩定,造成大量的成本浪費。因此,一般說來,一但一個算法被証明成熟並且入庫,則以後類似場合,只要沒有太大的性能差異,都應該無條件采用,不允許重新編寫。」
這個說法,讓我想起《修改代碼的藝術》(Working Effectively with Legacy Code),作者定義「遺留代碼」(Legacy code) 就是那些沒有編寫相應測試的代碼。
在不同的情況我們需要修改代碼,例如重構(改善現存代碼的設計)、增加/修改功能、優化效能等等。一個好的軟件工程,應該可以不停適應新的需求。有測試代碼就可以勇敢地去候修改代碼。
所以我不贊成要避免修改代碼,相反要努力修改代碼去改善軟件質素。
== 結語 ==
之前讀了3章就放棄,能讀完這一本書,對我而言是一個挑戰。當然,寫這麼長篇的評論,更是一個挑戰。
本書是集作者十多年的軟件開發經驗而成的作品。這本書可以讓讀者們更清楚了解到,目前中國軟件行業的一些狀況。
書中經常提及一個重點,要獨立思考、分析問題,作出合理、甚至是創新的解決方案。但是,本書所缺乏的,卻是一種求學的態度。
常言道,我們站在巨人的肩膀上。我們先要認識自己的渺小,要站得更高,先要慢慢爬上那個巨人上。
== 封底 ==
(總結的結語後還有封底......)
讀過封底關於本書目標讀者的句子,我就「突發奇想」:
「如果你是一名初次进入职场的软件专业学生,本书可以助你迅速掌握企业商业化开发的思路和技巧。」
你可能會在面試中遇到一點障礙。
「如果你是一名C和C++的学习者和爱好者,本书可以助你掌握很多实际的技巧,并获得一个现成可用的工程库。」
你可能會發現 C 和 C++ 的兼容性很大,可以把 C++ 當作 C 來用。
「如果你是一名商业公司的程序员,已经掌握了很多商用开发的思维和技巧,本书能给你一点新的、意想不到的提示。」
你可能學會了突發奇想,和別人分享一些常識。
「如果你是一名网络游戏公司的开发人员,本书的多任务工程库可能会对你很有帮助。」
你可能要考慮應否用幾千個時間中斷來更新幾千個遊戲物件。
「如果你是一名商用服务器的开发人员,本书可以助你掌握如何利用C和C++实现7×24小时稳定性的服务器的技巧。」
你可能會重新定義稳定性或者甚麼叫做 bug。
「如果你是一名嵌入式开发人员,本书中严格的代码规范和数据边界意识,对嵌入式之类资源较少且有长期运行要求的设备开发很有帮助。」
你可能會用一個全局內存池分配器服務所有綫程。
「如果你是一名使用其他语言的程序员,本书中很多基本模块的突现,如内存池、线程池等,对Java等程序员理解自己平台的相同模块,很有帮助,并且,本书提出的商用开发思想,是跨语言跨平台的,也很有参考价值。」
你可能會覺得選擇做Java程序員真的選對了。
「如果你是一名产品经理、项目经理或者架构师,本书中提出的很多商用系统工程的设计理念,是作者多年开发的经验结晶,对于系统的设计、商用项目的风险管控,有很好的参考作用。」
你可能會增強自信心。
0 bug的评论




2
(如果不知道在下這超長書評的始末,建議先看 http://www.douban.co
第4-6章書評 http://www.douban.co
第7-8章書評 http://www.douban.co
第9-12章書評 http://www.douban.co
雖然這本書使我有點失望,無可否認,它是獨特的。
第一,作者公開了他的商業用代碼,相信沒有很多國內公司會批准這麼做。這讓同行可以窺探競爭對手的狀況,所謂知己知彼,百戰百勝。
第二,本書描述的範疇,確實是國內比較少見的。從內容來說,個人認為較到題的(但會賣得不好的)書名應該是《C(++)伺服器程序庫開發》,範疇來說接近《C++網絡編程》,但減去網絡、理論及++。
第三,本書能令國內程序員(作者除外)間討論許多技術性話題,也讓我們去獨立思考求證,網友間互相學習。我懷疑是內地 IT 出版史上的一個標誌性刊物。
意見其實比較多,時間關係,以下輕談一些簡單的。
== C/C++ ==
在我第一篇評論裡,因為主要是指出1-3章懷疑錯誤的地方,我沒有提及對本書 C++ 代碼的意見。但我和很多讀者都有一些微言。看畢全書後,個人有以下分析。
面向對象 (Object Oriented) 在書中是重新被定義的 (見 P. 216)。個人認為,本書使用 C++ 的方式,比較接近 Object-based 語言 (http://en.wikipedia.
但個人覺得,在這種情況下,又不使用 C++ 的標準庫,單純用 C 更能使程序實現和介面變得簡單,而同時保留了資料封裝。舉例:
// C++ 版本 header
class Mutex
{
public:
Mutex();
~Mutex() throw();
void Lock();
void Unlock();
private:
Mutex(const Mutex&); // 防止 copy construction
Mutex& operator=(const Mutex&); // 防止 assignment
HANDLE m_Mutex;
};
// 使用 C++ 版本
Mutex* m= new Mutex;
m->Lock();
m->Unlock();
delete m;
// ---------------------------------------
// C 版本 header
struct Mutex; // 前置聲明
Mutex* MutexCreate();
void MutexDestroy(Mutex*);
void MutexLock(Mutex*);
void MutexUnlock(Mutex*);
// C 版本 implementation (不用在對外 header 定義)
struct Mutex
{
HANDLE m_Mutex;
};
// Uisng C version
Mutex* m= MutexCreate();
MutexLock(m);
MutexUnlock(m);
MutexDestroy(m);
雖然這種比較不完全等價 (C++ 可以創建非指針的Mutex型別),但可以很清楚看到,C 版本的 API (在 header 裡的函數定義) 比 C++ 還簡潔,而且跨平台部份(那個 struct) 也不會顯露在 header 裡 (這有點不準確,C++ 可用 Pimpl 手法,不過就更複雜了)。
使用時,C 版本的懷處是要在每個函數前加 Mutex 前綴,但這樣可能還增加了可讀性。
我的結論是,這種 object-based 方式用 C 語言比較好。用 C++ 去封裝這樣的 C 的 API 的作用不大,只會更複雜。
書中因為沒有重載 operator new 或使用 replacement new,出現要 C++ 類內部使用 malloc() 形式去分配 struct 成員變量,使代碼更複雜及難閱讀。簡潔的代碼是避免出錯的關鍵。
== 防禦式編程==
書中的代碼,經常用 if() 來檢查一些正確程式不會出現的情況。例如最常見是一些 precondition:
// P. 336
bool CTonyBuffer::SetSize(int nSize)
{
if (!m_pMemPool) return false; // 防禦性設計,如果內存池指針為空,無法工作
// ...
}
在該類裡,m_pMemPool是在建構函數就指定的,之後該對象的生命周期裡不能改變這個變量。所以在正確的程式裡 m_pMemPool == NULL 不應該在運作期出現。出現的話,就是代碼本身有錯誤。
使用 return 去逃避這個錯誤,會使那個 bug 更難被發現 (例如調用者沒有檢查傳回值,又或者調用者檢查到錯誤又繼續逃避這個錯誤)。
比較好的方法是立即終止程序,並提示在那個函式出錯。沒錯,這就是 C 的 assert()。而實際應用時通常會客制化這個函式(實際上是宏)。測試沒發現錯誤的話,可在發行版忽略 assert 去減少 overhead。
減少這種 if 亦等同減少代碼的複雜度和測試難度(一些複雜度 metric 包含計算有多少個條件分支)。
簡單來說,應盡量讓 bug 浮現出來。
== 修改代碼 ==
書中多個章節都強調,盡量不要修改代碼,例如 P. 100 「......任何代碼的修改、替換都可能造成新的不穩定,造成大量的成本浪費。因此,一般說來,一但一個算法被証明成熟並且入庫,則以後類似場合,只要沒有太大的性能差異,都應該無條件采用,不允許重新編寫。」
這個說法,讓我想起《修改代碼的藝術》(Working Effectively with Legacy Code),作者定義「遺留代碼」(Legacy code) 就是那些沒有編寫相應測試的代碼。
在不同的情況我們需要修改代碼,例如重構(改善現存代碼的設計)、增加/修改功能、優化效能等等。一個好的軟件工程,應該可以不停適應新的需求。有測試代碼就可以勇敢地去候修改代碼。
所以我不贊成要避免修改代碼,相反要努力修改代碼去改善軟件質素。
== 結語 ==
之前讀了3章就放棄,能讀完這一本書,對我而言是一個挑戰。當然,寫這麼長篇的評論,更是一個挑戰。
本書是集作者十多年的軟件開發經驗而成的作品。這本書可以讓讀者們更清楚了解到,目前中國軟件行業的一些狀況。
書中經常提及一個重點,要獨立思考、分析問題,作出合理、甚至是創新的解決方案。但是,本書所缺乏的,卻是一種求學的態度。
常言道,我們站在巨人的肩膀上。我們先要認識自己的渺小,要站得更高,先要慢慢爬上那個巨人上。
== 封底 ==
(總結的結語後還有封底......)
讀過封底關於本書目標讀者的句子,我就「突發奇想」:
「如果你是一名初次进入职场的软件专业学生,本书可以助你迅速掌握企业商业化开发的思路和技巧。」
你可能會在面試中遇到一點障礙。
「如果你是一名C和C++的学习者和爱好者,本书可以助你掌握很多实际的技巧,并获得一个现成可用的工程库。」
你可能會發現 C 和 C++ 的兼容性很大,可以把 C++ 當作 C 來用。
「如果你是一名商业公司的程序员,已经掌握了很多商用开发的思维和技巧,本书能给你一点新的、意想不到的提示。」
你可能學會了突發奇想,和別人分享一些常識。
「如果你是一名网络游戏公司的开发人员,本书的多任务工程库可能会对你很有帮助。」
你可能要考慮應否用幾千個時間中斷來更新幾千個遊戲物件。
「如果你是一名商用服务器的开发人员,本书可以助你掌握如何利用C和C++实现7×24小时稳定性的服务器的技巧。」
你可能會重新定義稳定性或者甚麼叫做 bug。
「如果你是一名嵌入式开发人员,本书中严格的代码规范和数据边界意识,对嵌入式之类资源较少且有长期运行要求的设备开发很有帮助。」
你可能會用一個全局內存池分配器服務所有綫程。
「如果你是一名使用其他语言的程序员,本书中很多基本模块的突现,如内存池、线程池等,对Java等程序员理解自己平台的相同模块,很有帮助,并且,本书提出的商用开发思想,是跨语言跨平台的,也很有参考价值。」
你可能會覺得選擇做Java程序員真的選對了。
「如果你是一名产品经理、项目经理或者架构师,本书中提出的很多商用系统工程的设计理念,是作者多年开发的经验结晶,对于系统的设计、商用项目的风险管控,有很好的参考作用。」
你可能會增強自信心。


2010-02-04 06:02:22 bookjohn
牛2010-02-04 06:03:57 bookjohn
请问milo,你是如何成为如此大牛的?2010-02-04 06:12:41 希锐亚
花这么长时间去写书评可要比看书辛苦多了。感谢楼主。另,楼主越来越客气以至很多时候没把问题直接说出来,此苦心希望肖老师能明白。
2010-02-04 08:11:50 西北
mark2010-02-04 08:47:56 当仁不让的老孙
如何能够将知识掌握到如此地步,令我不禁茫然。。。。。。差距在哪里呢?
2010-02-04 09:06:57 锲而不舍求妹汁
从这《封底》部分,看到Milo幽默的一面,哈哈~~2010-02-04 10:11:15 古月圣
zan 封底,呵呵2010-02-04 10:14:44 hoterran
mark2010-02-04 10:21:44 lorking
2010-02-04 08:47:56 天上地下唯我独尊如何能够将知识掌握到如此地步,令我不禁茫然。。。。。。
差距在哪里呢?
--------------------------------------
差距无非在人家把你喝咖啡的时间用在喝啤酒上
2010-02-04 10:53:40 leaveye
精彩,酣畅,一气呵成。顺道帮 milo 前辈修正两个别字。
浮现在来 -> 出来
什么只做bug -> 叫做
2010-02-04 10:56:13 leaveye
阅读 milo 前辈的书评,让我注意到了自己的几个做法不好的地方。收获很大。
惭愧,以前读书少,又不求甚解了。
2010-02-04 11:09:21 Milo
@leaveye謝,已改正。今早寫到五點,有點神志不清了。
2010-02-04 11:10:44 [已注销]
2010-02-04 10:56:13 leaveye阅读 milo 前辈的书评,让我注意到了自己的几个做法不好的地方。
收获很大。
惭愧,以前读书少,又不求甚解了。
=====
看书多也不代表能应用知识……
我认为看书,特别是技术书应该有三种层次的境界:
1. 看过;
2. 看懂;
3. 看薄。
貌似只有到了第三层才能进入厚积的状态。
到了薄发的时候才不会“书到用时方恨少”。
我还是继续和自家书架上的几十本技术书做艰苦卓绝的斗争吧……
2010-02-04 11:43:21 风中有只凌乱鸡
最后的时候,milo很明显的发飙了。。。2010-02-04 11:54:18 akirya
第7-8章書評 http://www.douban.co第9-12章書評 http://www.douban.co
两个连接贴成一样的了
2010-02-04 11:58:04 Milo
@akirya 修正了2010-02-04 12:10:03 [已注销]
很佩服Milo的耐心和耐力,这样写书评,真是让我收获甚大。以往读书不求甚解,得改啦。
2010-02-04 13:11:53 [已注销]
書中多個章節都強調,盡量不要修改代碼,例如 P. 100 「......任何代碼的修改、替換都可能造成新的不穩定,造成大量的成本浪費。因此,一般說來,一但一個算法被証明成熟並且入庫,則以後類似場合,只要沒有太大的性能差異,都應該無條件采用,不允許重新編寫。」這個說法,讓我想起《修改代碼的藝術》(Working Effectively with Legacy Code),作者定義「遺留代碼」(Legacy code) 就是那些沒有編寫相應測試的代碼。
====
这一段,和技术没有直接关系,却想和大家一起探讨一下。
我做外包的,外包在项目管理中有一条不成文的规定,“既有代码中发现Bug,不能立刻修正,必须走严格的确认流程,确认修改本身不影响业务逻辑后才能计划修正。”
对于已经商用的代码,客户的要求更严格,“不准修正,直到下一次开发周期到来时才纳入计划,或继续推迟。”
而在实践中发现,其实这些代码,都有很多逻辑/功能/需求上对应的测试用例和数据集的。
我想发问的是,代码应该于何时修改?为了什么而改?成本几何?
是否为了改善性能而改?还是为了修正Bug而改?还是单纯的有技术洁癖而改?
而且所谓的“遗留代码”本身,也不是那么简单地确定的,毕竟产品已经在使用中了。
这已经不属于技术的范畴了,个人认为改代码应该属于工程管理范畴(或者更上一层,业务需求范畴),在确认和实施上应该更小心谨慎,而且对于程序员这一执行者来说,尤其要慎之又慎。
一个做电力软件的同学也持相同的看法,“要是产品都已经发布了,难道还要‘召回’代码再重新链接么?”
就这一点上来说,其实肖舸讲的还是有理由的,就国内的软件业应用环境来看,确实如此。
2010-02-04 13:28:21 功夫羊
这完全两码事,产品版本发行计划和defact管理流程,跟要不要改进legacy code,并不冲突的。2010-02-04 13:32:58 [已注销]
在日本人的工业系统级产品里,通宵没有版本的明确概念,只有开发阶段的概念,我们接项目都是一个Phase一个Phase地接的。在接的时候经常会遇到改还是不改的问题,也经常在这个问题上被日本人折磨,有说要改的,有说不要改的。
只是想知道有没有现实例子具有很强的说服力,好说服日本的客户去修正代码。
2010-02-04 13:35:19 Milo
@无锋之刃如我之前所說,通常有幾種原因要修改代碼。你問的幾個問題都是工程管理裡要做的決定。可能軟件發佈了,才發現新的 Bug,那可能要評估那個 bug 的影響,有沒有 workaround、hotfix 的可能,是否是要通知客戶等等......
我想指出的是,應該持有修改代碼的開放態度,有可能要跟據時間、人力、風險等等評估,甚至乎是實驗性修改後作出測試才考慮是否合併到 main branch裡。不是以「只要沒有太大的性能差異,無條件采用,不允許重新編寫」為準則。
2010-02-04 13:47:21 [已注销]
@Milo可能我理解得有点偏了,没问清楚。你所以指出的对待“修改代码的开放态度”,确实是点到了关键上,后面说的方法也很切合实际,但,在国内,这未必是主流,毕竟和承包方公司的开发理念、管理手法以及最终客户的成熟程度为前提的。至少,日本客户对我们做了什么样的开发了若指掌,而内地的客户甚至对自己的需求都不甚了了。
其实改Bug中能量化评估的几个方面你都点到了,只是这样的态度和情绪很难培养,尤其是对新入行的新人来说,Review都很抗拒,更遑论让他改Bug了。沟通的成本很高,说服对方采用这样的理念也很难,我想知道你们在技术或者工程管理方式上有没有好的说服方法来降低这样的试错成本?
肖舸提到的「只要沒有太大的性能差異,無條件采用,不允許重新編寫」确实是很多人反对修改自己代码的一个理由,包括以前的我。
若是问得偏了,望见谅。
PS:其实这个问题涉及到的方面甚多,甚至估工作量确认接包量的时候都必须考虑到,而且高质量的人力资源实在太紧张了。
2010-02-04 13:50:33 [已注销]
对于“有測試代碼就可以勇敢地去候修改代碼。”,我持保留看法,应用的软件开发领域不同,这句话是否适用值得再三思量。2010-02-04 16:30:05 当仁不让的老孙
这和公司的缺陷管理规定,版本管理规定,代码管理规定和重构管理规定有关。程序员没有决定权。
2010-02-04 16:48:39 [已注销]
人手紧张或项目经理对项目不熟悉的时候,会让工程师直接参与到项目估算的环节里来,这在外包行业很常见。当然决策还是在于经理以及项目导入经理。
2010-02-04 17:15:11 Milo
@无锋之刃 @天上地下唯我独尊我覺得職位名稱(title)不重要,最重要是當時戴那一頂帽子(role)。
2010-02-04 20:38:25 肖舸
我的新书《0 bug -- C/C++商用工程之道》近期已经面市,经过出版社统计,上市一个月20天左右,销售共计2500多册(出版社数据),这在专业性技术书籍中,应该还是比较乐观的。我作为作者本人,也衷心感谢各位读者的鼓励和支持,我将一如既往地努力进行后续版本的修订和写作,为大家提供更好的精品书籍。应该说,我写这本书,还是有一定目的的。
目前的社会上,讲C和C++语言的书籍汗牛充栋,但是,我发现有个问题都没有讲,就是“并行计算”。
很多学习计算机软件编程的朋友,在学校中学习到了很多很好的知识,但是,就笔者所知(可能是笔者孤陋寡闻),确实很少有大学开设《并行计算》这门课程。
但我们知道,现实社会中,目前32位多任务操作系统,如Windows、Linux、Unix等操作系统已经完全普及,哪怕连很小的嵌入式设备,如手机上,都开始使用多任务开发环境,这就要求现代程序员必须具有并行程序设计能力,但无疑,目前大多数程序员缺乏这种能力。最直观的例子就是Intel等CPU厂商,为了自己的多核CPU好销,已经开始在各地区开设程序员进修班,以各种形式向大家培训并行程序开发技能,以便解决整个业界无法适应多核多任务开发环境的需求这个问题。
我也是在这种情况下考虑写作本书的,本书虽然定位于0bug,无错化开发,宣导商业化的务实开发思维,但这仅仅是一个方面,传统单任务程序设计的无错化方法,其实已经有很多参考资料了,笔者认为再“炒剩饭”没有多大意义。笔者从第一天开始写作本书,就致力于解决大家关心的一个核心问题:“如何书写无错化的并行程序?”,这应该说是本书的核心宗旨。
这么做的意义显而易见,由于现在是互联网的社会,网络化开发采用C/S模型,但是,大量的书籍讲了Client端的开发和设计,缺很少,甚至没有书籍来描述服务器端的设计技能。但偏偏目前业界几乎所有的应用,都已经逐渐网络化,在未来的集中式和分布式运行模型下,大量的设计需求要求程序员具有多任务服务器的设计能力,这是一个现实的情况。
现在,哪怕一个很小的嵌入式家用路由器,其实都要求具有7*24小时的连续服务能力。这很好理解,大家想想自己家里的小路由器,买回来连通后,是不是很少断电和关机?
所以我作为作者,才认为这本书有这么大的现实意义,我是商用程序员,做事情讲究务实,我写书,也希望切切实实帮助现在的大多数程序员解决自己身边,现在就遇到的问题,因此我从这个角度切入,写作本书。据某些读者朋友反映,本书是:“目前为止我知道的惟一一本关注服务器端程序设计的 C++ 书。而且又是国人的原创作品,十分难能可贵。”
不过呢,俗话说,林子大了,什么鸟都有,网络作为现实社会的一个剪影,确确实实什么人都有的。本书出来后,一直受到很多不必要的干扰,这个,我在博文《关于《0 bug-C/C++商用工程之道》一书出版前后的故事》中,已经有了详细论述,此处就不再详述。
但是,我作为作者,也考虑了,不能把提批评意见的朋友都一竿子打死,这也不是客观的科学态度。毕竟,所谓“枪手”之说,我的猜测居多,并没有什么铁证,只能参考一下。因此,我又静下心来,详细阅读了一些读者,特别是一些“大牛”级的读者的批评意见。希望能找出一些真正属于自己的错误,好修订本书。这也是本着为读者负责的态度。
但是,很奇怪,我发现不管是这些大牛,还是一些小牛,批评的意见大体有个总体思想,就是本书不符合C++开发的主流规范,显得“不标准”,“野路子”。这差不多也是目前批评本人和本书最主流的意见了。这让我这个作者莫名其妙,先不论这些读者是好心还是恶意,也不论他们批评的意见是否正确,但这些意见显然我无法接受。
原因很简单,请大家看本书的书名《0 bug -- C/C++商用工程之道》,这表明,本书源代码其实是用C和C++这两种语言开发完成的,并且,在本书中任何一处提到开发语言的时候,C一定排在C++语言前面,如“C和C++无错化开发方法”。
就我这个作者本人而言,在平时工作中,我比较喜欢同时使用C和C++两种语言混合开发,当然,以C为主。这一来是工程有时候需要,二来是我个人开发习惯,毕竟,我是先学会C语言,后来才学习的C++语言。
我想这也很好理解,从第一天开始,C和C++语言编译器就是兼容多种语言的,我们知道,几乎所有版本的C和C++语言编译器,都支持内嵌式汇编开发,这是合理的,因为工业控制中很多高速端口操作,需要汇编来完成。所以现代的C和C++编译器,也有逐渐大一统的趋势,无论是VC还是gcc,均支持混合语言编程,可以说,目前我们的主流编译器,基本上都兼容三种语言,C、C++和汇编。
因此,请各位读者就事论事,在阅读过程中,不要用纯正C++语言来考察本书的源代码,本来就不是主要用C++语言开发的。
其实,我分析了一下,在我的源代码中,C和C++的比例,差不多8:2,即80%左右使用C语言开发的。对于C++语言,我本人倾向于“有限使用”,这个呢,是我的习惯,我习惯于到具体功能点的实现,使用C模式,因为即使是C++语言,函数内部都还是OP的,即面向过程的,这符合实际需要,但在模块组织上,我比较喜欢使用C++的对象概念来封装,因为确实方便。
实际上,我曾经想过,是不是以本书定名就定为“C语言商用工程之道”,但显然也不现实,因为书中确实提到了对象。
根据我个人经验呢,这比较符合现代商用系统的开发模型,一个较为大型的系统,尤其是网络相关,很多时候,系统是多种语言的混合体,有C和C++的,还有PHP和Java的,客户端开发还经常使用JavaScript和C#等,这些,我认为都是合理的。这毕竟是一个全方位满足客户需求的综合开发时代。
另外,我这里也说说本书的源代码,很多读者知道,本书虽然内部包含大量带来,甚至包含一个并行开发工程库,但我并没有提供源代码下载服务,也不提供源码光盘,很多朋友都问我为什么,其实我是有原因的。
1、本书定位为一本工程实战书籍,我认为更多是讲思路,讲解法,即share我本人的一些经验,帮助大家在以后的工程开发中,能有一些解决思路,能解决具体问题。因此,我没有认为本书的源代码有多重要。
2、本书的源代码,我本人认为它是一种语言,由C和C++语言代码,以及相关注释,以及相关文字描述共同组成的一门语言,是程序员写给程序员看的一门语言,是用来讲清楚问题的,不是拿来就用的。就好比我们上学时的伪代码,是描述逻辑使用的,仅仅是我这个作者图省事,把自己的代码直接拿出来,做了注释就写到书里了。
3、我本意是写书,不是做开源,如果做开源,我直接在网上开个库供大家下载好了,不需要写书这么麻烦的。
4、本书中代码,最少的都有两年无故障连续运营历史,最多的有9年。但我仍然不认为这些代码就一点bug都没有。只能说,在过去的工程环境下,没有暴露出bug而已。这在书中已经说明了的。
5、本书讨论的0bug,我有专文说明,一来,0bug我认为是程序员应该有的一种追求,是目标,其实我本人也没有做到的,但二来,本书讨论的0bug,可能比大多数人讨论的严厉一点点,即产品卖出钱了,你把钱揣到包包里面,并且落袋为安,不会因为维护再花出去,这个叫做0bug。我想我已经说得够清楚了,此处再次重申一下,就不劳大家不断争论了。
此处呢,我作为作者,在此做个郑重声明,也希望各位读者和准读者朋友能精确看清楚本书的定位,以及写作的目标,有的放矢地购买和阅读本书,以便更好地学有所获。
==================================================
(以下文字,纯属虚构,如有雷同,实属巧合)
From: Mxxx Yxx <xxxxxx@gmail.com>
Date: 2010-2-2 12:28
Subject: Re: 关于程序中需要用锁的原因
To: xxxx <xxxxxx@gmail.com>
x先生你好
謝謝賜教。
我對於硬體的知識很有限。我以為cache是其中一個可能做成不同步的原因。
不同的 architecture 下的同步機制會有出入,行為會有出入,所以我認為編程時應該使用平台提供的方法,而不要去假設一些行為。
RISC 的 load/hit/store 是會造成不同步,但書中說的不一致的例子是: 一個 32-bit data 寫到
memory,只寫了16-bit,另一個 thread 就去讀取。這個情況我覺得在現代的系統裡應該不存在的。
所以我才建議,如果不需要就不要寫一些底層的機制,讓讀者明白一組內存/設備要同步時使用同步機制便可以了。
如果願意, 閣下可以把這討論放到豆瓣, 我轉載也可以的。
再次感謝x先生的來信指導。
在 2010年2月2日上午11:45,xxxx <xxxxxx@gmail.com> 寫道:
> 昨天看了您在豆瓣上的书评,以及后面的争论。关于0bug的
> 有个技术问题跟您讨论一下。
> 您说的关于程序中需要用锁的原因,是由于smp系统中存在cache。我觉得,这个论断是不正确的。
> 大多数现代的smp系统,包括多核、多CPU系统,应该都是在硬件解决了这个问题。
> 比如,小规模的系统,用总线侦听协议,如:MESI协议。
> 而大规模系统,则用目录协议来解决这个问题。
> 所以一般来说,在软件实现的互斥锁中,并不会有一个显式的cache同步指令。
> 即便是在没有cache的单CPU、单核系统中,也可能存在多线程之间的数据不一致的问题。
> 例如:我们有一个简单程序,一个线程循环执行i++,另外一个线程执行i--,两个线程的循环次数相等,这两个线程的循环次数足够的大的时候,运行完毕之后,i的结果可能不等于初始值。
> 这是因为,某些RISC体系的CPU,load和store指令是分开的,当一个线程执行了load之后,如果被另外一个线程打断,此时就会出现我们不期望的结果。
> 即便在x86上,也可能出现,因为对多数语言并不会去定义,i++这条语句应该对应一条什么样的汇编指令。
> 尽管大多数x86上的编译器会把i++编译成一条inc mem[imm]指令,但这个是不保证的。程序正确性不应依赖于某个特定编译器。
>
下面是MSN的讨论记录:
xx 说:
打扰
xx 说:
昨天看了您在豆瓣上的书评,以及后面的争论。
xx 说:
关于0bug的
xx 说:
有个技术问题跟您讨论一下。
xx 说:
您说的关于程序中需要用锁的原因,是由于smp系统中存在cache。我觉得,这个论断是不正确的。
xx 说:
大多数现代的smp系统,包括多核、多CPU系统,应该都是在硬件解决了这个问题。
xx 说:
比如,小规模的系统,用总线侦听协议,如:MESI协议。
xx 说:
而大规模系统,则用目录协议来解决这个问题。
xx 说:
所以一般来说,在软件实现的互斥锁中,并不会有一个显式的cache同步指令。
xx 说:
即便是在没有cache的单CPU、单核系统中,也可能存在多线程之间的数据不一致的问题。
xx 说:
例如:我们有一个简单程序,一个线程循环执行i++,另外一个线程执行i--,两个线程的循环次数相等,这两个线程的循环次数足够的大的时候,运行完毕之后,i的结果可能不等于初始值。
xx 说:
这是因为,某些RISC体系的CPU,load和store指令是分开的,当一个线程执行了load之后,如果被另外一个线程打断,此时就会出现我们不期望的结果。
xx 说:
即便在x86上,也可能出现,因为对多数语言并不会去定义,i++这条语句应该对应一条什么样的汇编指令。
xx 说:
尽管大多数x86上的编译器会把i++编译成一条inc mem[imm]指令,但这个是不保证的。
xx 说:
程序正确性不应依赖于某个特定编译器。
xx 说:
稍等,去开会。
xx 说:
回来再讨论。
xx 说:
hi
xx 说:
关于为什么要用锁的问题,可否这样解答。
xx 说:
在多线程环境下,多个线程之间共享内存中的对象。
xx 说:
程序运行的正确性,依赖每个线程对于内存对象的更改操作的原子性。
xx 说:
用锁的目的,就是为了保证原子性。
xx 说:
而非原子更改操作产生的原因是多方面的,如:肖先生所说的原因,多个byte分开操作,是一方面的原因。
xx 说:
您说的smp体系下cache不一致性的问题,是导致这个问题的另外一个原因。
xx 说:
究竟应该用何种类型的锁,是由编程环境所定义的内存一致性模型决定的。
xx 说:
对象原子性被破坏的问题,是表现在不同层次上的。
xx 说:
他们之间又是相互联系的,如:底层内存访问的不一致,可能导致编程语言和操作系统层面不一致。
xx 说:
例如,您说的cache一致性问题,可能被传导到上层编程语言的层面。
Mxxx 说:
要正確說明為甚麼要用同步機制, 可能是挺困難的. 我有空的看看相關書籍的說法吧.
xx 说:
至于究竟应该用那种类型的锁,是由编程环境决定的,例如:如:PowerPC体系结构采用“释放一致性”模型,是比较松散的。
Mxxx 说:
atomicity 和 consistency 好像是兩個不同的 quality
xx 说:
而其模型,则要严格些。
xx 说:
是的,这是不同的概念。
xx 说:
似乎你对中文技术词汇,不是很熟悉
Mxxx 说:
小時候主要看台灣的, 現在又看內地的, 都混亂了
xx 说:
关于那本书,我没看过
xx 说:
但是那个人,我认识
xx 说:
我觉得要全面评价一本书籍,是比较困难的事情。
Mxxx 说:
這當然了, 不然就不會有評論家了.
xx 说:
您之所以产生“失望”的感觉,是因为那本书不太适合您。
Mxxx 说:
我希望可以比較客觀地在總結裡寫本書的優缺點
xx 说:
如果让一个成年人去读儿童读物,就会比较“失望”
xx 说:
当然,也并不是说儿童读物没有价值。
Mxxx 说:
其實我看前三章時, 真的感到書裡的不嚴謹, 只是靠經驗去解決問題, 也叫讀者這麼做.
xx 说:
这是他的习惯,我并不赞同。
xx 说:
但,返回头来说,这本书可能会让另外一些人学习到一些经验。也未尝不是好事。
xx 说:
我相信,每个人读书,对其内容都是选择性的读。不会是全盘接受的。
xx 说:
如果肖先生能够再谦虚一点,也许更好。
xx 说:
打扰了。
--
Mxxx Yxx
2010-02-04 22:50:44 猛禽
围观作者变身----2010-02-04 22:52:51 老赵
@----肖老师,您怎么换个马甲又来了呢?
您之前一直骂别人不用真名用马甲,那么您这个又算什么呢?
不知道您这次还删不删,我懒得截图留念了……不过您要知道,删贴如自宫啊。
2010-02-04 22:57:56 beyondwdq
肖老师应该正正当当地出来讨论问题,既然是楼主和另外一位老师的讨论,又何必写上“(以下文字,纯属虚构,如有雷同,实属巧合) “。我估计包括楼主在内,大部分人都没有认为0bug这本书一无是处,相反,肖老师在这次讨论中表现出来的态度,确实让人汗颜。正如xx说的:如果肖先生能够再谦虚一点,也许更好。2010-02-04 22:58:09 润闰
继续围观2010-02-04 23:05:59 Milo
哈哈. 雖然沒同意過公開 MSN 記錄,因為交談比較寬鬆,怕一些沒想清楚的技術沒有考証, 寫錯了會不好. 不過我問心無愧, 也沒有甚麼不能公開的. 實在想不到肖先生覺得那 MSN 記錄有甚麼問題 :)2010-02-04 23:16:33 老赵
@Milo还莫名奇妙地xxxx,真x就算了,还Mxxx Yxx,真是……
2010-02-04 23:28:24 Milo
@Jeffrey Zhao我也想了解一下某人的心理。了解一個人不容易啊。
2010-02-04 23:47:33 Terry
这段对话想说明什么?谁能解释下中心思想?
2010-02-05 08:45:31 锲而不舍求妹汁
我还以为是谁……2010-02-05 08:56:22 lorking
忍不住说两句题外话:肖老师突然跳出来说这么些子话,很不高明啊。正确的做法是,吸取教训,卧薪尝胆,认认真真的把书重写一遍。
有句话扯远了,中国人对突发事件的处理,对危机的处理真是比国外那些人差远了,不论精英到草根均是如此。
2010-02-05 09:08:19 knight_stalker
肖老师是何等的师太 …… 突然让我想起这个:http://www.acfun.cn/
2010-02-05 09:08:40 宝剑锋
围观。。。。。。。。。。。。。。2010-02-05 09:27:12 肖舸
话说某日,我正在归妹位看书。突然,一声怒喝,某人左手板砖,右手大刀片子,身上挂着要你命3000,奔着我就过来了,吓我一跳。
他身后,还跟着一群打了鸡血的小公鸡,一个个鸡冠子竖老高,红得发紫,亢奋得嗷嗷直叫。这叫助威团。
我说大事不好,正待摆出架势接招。
没想到某人行至中途,突然刀锋一转,直接砍到无妄位的电线杆子上去了。
可怜的电线杆,脑门上挂了个“C”。
随后,板砖,脏水,烂西红柿,臭鸡蛋... ...
电线杆惨不忍睹!
我看了一下,觉得无趣,就走了... ...
一个星期以后,我突发奇想,就回去看看。
电线杆子还在,某人还在继续劈砍,不过,显然后继乏力。
助威团呢,还剩下小鸡两三只。鸡冠子也垂下来了。毕竟,从生理学上讲,雄起得太久,会钙化的,呵呵。
我看了一下,觉得无趣,就又走了... ...
... ...
... ...
“收工!”,随着导演一声令下,摄影棚一片忙乱。
“碰”,道具关闭了电闸,灯火通明的摄影棚暗了下来。
人声渐行渐远,摄影棚安静下来。
墙边的小门被微风吹开,一缕路灯的灯光,射了进来。
凄白的灯光,射到摄影棚中心的道具“C”电线杆上。
杆下靠坐着某人,嘴里喃喃地说:
“哥拍的不是C,哥只是想成为传说... ...”
----仅以此文,纪念2010年春,某人以C++拍C,以及替伪代码debug的神勇之战
2010-02-05 09:28:52 hiessu
一个星期以后,我突发奇想,就回去看看。----------------------------------------
又是突发奇想。。。。
2010-02-05 10:45:25 sprite
肖老师你怎么变成四根横杆了?2010-02-05 16:54:07 George Wing
拜一下 Milo 大牛,祝愿本人今年能在编程(包括基础和经验)上有所突破~2010-02-05 17:18:56 George Wing
Tiny fool 也拜拜~两位大牛:希望能让本人今年能在编程(包括基础和经验)上有所突破~谢谢~
2010-02-05 20:40:36 Loaden
// P. 336bool CTonyBuffer::SetSize(int nSize)
{
if (!m_pMemPool) return false; // 防禦性設計,如果內存池指針為空,無法工作
// ...
}
=================
请教一下Milo先生:这里我更倾向于打印log,而不只是简单的assert。
有的时候,我们可能保证不了一定成功,打包log,并且附上代码行数,是我喜欢的处理方式。
请问:打包log的方式缺陷在哪里?谢谢!
2010-02-05 20:42:07 Loaden
抱歉,上面回复中的“打包”应该改为“打印”或“输出”。我一般是在测试阶段输出到控制台,发行时输出到文件。
2010-02-05 21:37:12 Milo
@Loaden>这里我更倾向于打印log,而不只是简单的assert。
所以我說「而實際應用時通常會客制化這個函式(實際上是宏)。」,用 log,或是其他方式也可以。例如在做以進程為單位的 unit testing 的時候,可能用 stdout/stderr 及 exit(errorCode) 去通知外面出錯。
另外,這種編程最困難的地方是要決定這種錯誤是內部造成的,還是外部造成的。例如在正式的工程中,你可能不應該在 fopen() 失敗時用 assert() 檢查是否有錯誤。因為這並不是軟件內部造成的錯誤,是需要用正常方法 (if/throw)處理的。
但寫多些 precondition, postcondition 及其他 defensive checking 可以有助找出軟件的問題。這些代碼亦可以作為閱讀用途,讓代碼的讀者清楚知道,比如某個函數的每個參數的可用範圍。
如果寫代碼時,覺得這個參數/變數「應該」是這樣那樣的,就用 assert 去「証明」吧。
2010-02-05 23:05:32 tingsking
世间竟会有如此无耻之人!2010-02-06 09:20:45 Loaden
@Milo 感谢您的指点!!2010-02-07 00:29:39 [已注销]
我居然错过这次围观, 不该! 不该!2010-02-07 22:15:30 selfredemption
肖的表现确实让我大感意外...2010-02-08 14:15:15 sushi
-----------------------------2010-02-05 09:27:12 ----
话说某日,我正在归妹位看书。
突然,一声怒喝,某人左手板砖,右手大刀片子,身上挂着要你命3000,奔着我就过来了,吓我一跳。
......
“哥拍的不是C,哥只是想成为传说... ...”
----仅以此文,纪念2010年春,某人以C++拍C,以及替伪代码debug的神勇之战
-----------------------------
哥......你改行写小说,说不定更有钱途。。。。
2010-02-08 17:25:41 iLRainyday
肖大师,您改名之后,开始胡言乱语了??2010-02-08 19:39:47 锲而不舍求妹汁
而且是意识流的小说~2010-02-10 11:09:46 琳琳的小狗
我写书,要是有此等好读者帮忙挑错完善,那做梦都会笑醒的诶……肖大师,您怎么就不知足呢!
这让我想起了以前在china-pub上遇到的李X门,不过那是关于一本rails书的,也是如此,好心没好报……
2010-02-10 11:23:50 iLRainyday
任何影响肖老湿书本销量以及”名声“的人,都是敌人,都是有阴谋的,又是别有用心的。2010-02-10 19:49:47 refactor
还归妹位,还神勇之战,真是装逼2010-02-11 18:18:00 mahoo
感想1:虔诚的登山者努力向顶峰攀登,回头看到身后一个人还没有爬到半山腰,就开始向还在山下的人介绍自己的登山经验,关键问题在于,他还想推销自己的书。如果他推销的是自己的登山诗集和拍摄的照片也就算了,但他想推销的是自己的登山指南,祈祷读者自身的鉴别力吧......感想2:写手们以之谋生,大师只留下经典,圣人是述而不作的......
2010-03-09 21:43:57 xyz
M2010-03-26 16:15:40 葡萄
mark2010-06-24 08:50:23 iSnow
非常有用,在争论中发现不足!2010-08-23 05:23:51 tingmei
Milo :真正的程序员,是我学习的榜样。我买了肖的这本书,一个字:悔!正想看看大家的意见,不留神看到了 Milo 的评论和这些回帖。真的是说了我想说的。但是, Milo 的涵养太好了,要是我,干脆二话不说,只要见到这个肖,见一次打一次!
这本书绝对是骗钱的,起了一个哗众取宠的书名……那个肖无论技术、人品、人格、态度、心胸……实在是让人鄙视!中国的程序员之耻!当记入史册!肖疴,我记住这个卑鄙的人了!
2010-11-28 19:35:27 mr.nop
我也很不幸的买了这书,呵呵,还看了一两遍,实在…………2011-04-19 22:13:31 wcdj
谢谢Milo的书评,我是在图书馆借的这本书,因为本人还是学生,觉得这本说的角度挺独特,但是书上确实有不少的错误。感谢您指出的错误!2011-09-19 21:20:17 xray
感谢milo,读了milo的几篇文章都受益很多,期待大牛的译作啊> 我来回应