[討論] 似是而非的程式設計觀點

樑濤發表於2011-11-29

[由來]
前段時間,我們維護的郵件閘道器係統被幽靈般的Alarm訊號偷襲,引發疑似Bug的故障。經過反覆審查程式碼、翻閱資料,最終確定問題可能出在下面這段Perl程式碼上:

sub unset_alarm_signal {  
    $SIG{ARLM} = undef;  # 1. 置空ALRM訊號處理函式  
    alarm(0);            # 2. 取消定時器,避免發射ALRM訊號  
} # unset_alarm_signal  

這一小段不起眼的程式碼究竟有什麼缺陷呢?

熟悉非同步程式設計和訊號機制的人大概一眼便看出問題所在:步驟1和2之間存在競態條件。假設執行流正好處於1和2之間的當兒有ALRM訊號被核心發射,此時原先設定的訊號處理函式已經被替換成undef(在Perl中undef對應的訊號處理函式即為系統預設的訊號處理函式),則呼叫unset_alarm_signal()的程式會立刻被殺死(對ALRM訊號的預設動作是殺死程式)。這一效果或許不是程式設計師所希求的,更可能會損害到系統正確性、健壯性。但是同事並不熟悉非同步程式設計和訊號機制,為了證明這個觀點我大費周章地翻找了不少書面資料,甚至動用Debug模式強制模擬出這一競態條件作為佐證。可是時間已經花去了不少。如果一開始,同事就很熟悉那些系統提供的程式設計機制和觀點,修正Bug所用的時間開銷將會大大減少。

最後採用修改方案如下:

sub unset_alarm_signal {  
    alarm(0);               # 2. 取消定時器,避免發射ALRM訊號  
    $SIG{ARLM} = 'IGNORE';  # 1. 採用Perl提供的忽略ALRM訊號的處理函式  
} # unset_alarm_signal  

為了證明這一修改有效,又折騰了一個來回。某個陽光明媚的暖冬午後便這般消逝了……

[危害]
由此我產生了一個想法:程式設計師不僅僅通過程式設計來闡述解決問題的方案,還藉由程式來驗證自己的程式設計觀點是否和他人一致。程式設計觀點不一致,在最好的情況下僅僅影響到程式執行效率,而在最壞的情況下會把終端使用者折騰到發瘋和憤怒。很明顯,處理這類破事兒會浪費大量時間和精力,以及信任關係。

更進一步,不一致的程式設計觀點,甚至危及開發團隊的團結和士氣。程式設計師往往都很聰明,或者自認為很“聰明”,被人指摘出錯誤或許是令人難堪和難以接受的。可以想象,招聘優秀又能相互理解彼此的程式設計師的難度不亞於建造一座“觀念”上的巴別塔。

[分析]
程式設計觀點到底從何而來呢?它們如何傳播?在傳播過程中,它們是如何被誤解、扭曲和放大的?細細思量,大致推想出這麼幾點:

  1. 常規教育/教材
    對於IT行業,尤其軟體開發領域,常規教育是不可或缺的一環。在專業知識儲備或實踐經驗不足的教師施教過程中,以及不嚴密細緻編撰的教材中,會引入各種主觀上已經誤解的程式設計觀點。在國內教育崇尚教師權威的現實環境下,這類有誤的程式設計觀點會直接影響到程式設計師新手的整體程式設計觀念,並延續上很多年。
    典型例子是譚浩強老師編寫的C語言教材。總體上來看這本教材編寫得不算優良,很多細節知識闡述得並不清楚,某些章節甚至將原本界線分明的概念混同起來。由於印量大,對於初學者的影響不可估量。

  2. 書籍
    未經認真編輯/審校/堪誤或翻譯有誤的專業技術書籍、追求銷量和短期盈利的口水技術書籍都是引入不良程式設計觀點的絕佳源頭。由於書籍具備權威性的特質,不加以分辨地吸收和濫用,讓這類不良程式設計觀點更隱蔽、傳播範圍更廣、影響更為深遠。

  3. 網路
    書到用時方恨少,程式設計觀點缺失或有爭議時,快速便捷的網路成為多數程式設計師解決問題的首選。但是快速的工具同時容易加速不良/有誤的程式設計觀點的傳播速度,並很可能放大其扭曲的程度。廉價的拷貝/貼上操作、不經實踐驗證的懶惰習慣以及語言/語境的隔閡混雜在一起,使得這類程式設計觀點充斥在各大社群/Blog之中,久久不能退散。這一點,只要用Google簡單地搜尋socket相關的程式設計知識便可以證明。
    可供參考的一個例子是,當初還混跡於CSDN的時候,曾經跟某人激烈爭辯TCP協議的安全問題。對方堅持認為TCP是強安全的,傳輸過程中不會產生錯亂;我則認為TCP只保證連續資料流按順序傳送到對端,對內容一致性沒有強制保證,因為TCP層的驗證和計算方法在兩個Word交換順序後不會變化。這將導致不同的報文內容計算出相同的驗證和,產生衝突。若要保證內容(語義)的一致性必須在應用層自己做驗證。這一點,不論是用Ethereal抓報文包分析還是直接看Linux協議棧演算法均能得到確認。雖然最後發現我們討論的並非嚴格意義上的同一個概念,時間卻白白浪費掉了。

  4. 同事
    工作以後的程式設計師更傾向於求助同事來解決缺失或有爭議的程式設計觀點,畢竟人肉搜尋比“機器搜尋+人眼過濾”快上許多,同時顯得更為可信。但在工作壓力之下,懶惰、不願意深入思考、沒時間動手驗證、期望從網路上隨手取用的念頭,導致很多人不會真正去檢視自己獲取到的程式設計觀點是否正確。這類程式設計觀點要麼在信任關係的掩護之下風平浪靜,要麼在某些極端的邊界條件下興風作浪。
    典型例子麼請參考前文。

  5. 文件
    聯機手冊、設計文件,還有林林總總的對程式、軟體、資料進行說明的各式文字,都是儲存程式設計觀點的好地方。理想情況顯然是所有正確的程式設計觀點都被良好地文件化。可現實往往是東寫一塊,西寫一塊,既不集中,也不全面,還可能缺失(想象一下程式設計師的優良美德:懶惰),甚至伴隨著時間的流逝,文件前後不一致、文件與實現版本不一致等各種異常情況也會產生出來。

[結論]
假如根據危害程度排列,我覺得網路上的不良程式設計觀點的危害性是最大的,其次是同事和文件,最後是常規教育/教材以及書籍。這一順序反過來,恰好是一個觀點產生、傳播、誤解、扭曲的完整過程。不管如何追溯,其源頭都是儲存和闡述程式設計觀點的書籍。那麼應對不良/有誤程式設計觀點的方法便有如下幾個:

  1. 多讀高質量的程式設計書籍,最好是外文原版的,或是經過同行反覆閱讀/強烈推薦,無論學習還是工作均應如此;
  2. 程式設計時遇到不明確的程式設計觀點,儘可能查閱手頭有的高質量權威書籍,根據這些已經經過大量人眼閱讀、確認、勘誤的文字中確認程式設計觀點正確性;
  3. 吸引新的程式設計觀點,或與已知的觀點衝突時儘可能動手實踐,通過試驗和實驗來驗證其正誤、理論與實現的契合程度(TCP/IP協議棧的實現是一個典型例子);
  4. 不輕易傳播拿不準的程式設計觀點,傳播時儘可能附帶上關於驗證環境和驗證過程的描述;
  5. 最終極的方法,保持對一切、尤其是似是而非的程式設計觀點的質疑,小心假設,大膽求證。

相關文章