C 多執行緒除錯和測試的注意事項

2021-08-23 13:53:38 字數 2444 閱讀 7100

在上次的帖子 聊了c++多執行緒的跨平台問題,後來感覺意猶未盡。今天順便說一下開發c++多執行緒應用程式時,有關除錯和測試的一些注意事項。下面這些注意事項主要是針對c++,不過有些對於其它的語言也適用。

關於設定斷點和單步執行

很多同學非常依賴於偵錯程式的斷點功能和單步功能。這在單執行緒情況下倒還好(不過有些單執行緒但涉及gui的程式,也會有點麻煩)。至於多執行緒程式的除錯,這兩種手段簡直就是噩夢的開始。多執行緒造成的主要問題大都和競態條件(race condition,詳細解釋看「這裡 」)有關。而設定斷點或單步跟蹤可能會嚴重干擾多執行緒之間的競爭狀態。導致你看到的是乙個假象。比如本來有兩個執行緒併發執行,存在某些不和諧的bug(由競態引起)。一旦你在某乙個執行緒設定了斷點,該執行緒在斷點處停住了,只剩下另乙個執行緒在跑。這時候,併發的場景已經完全被破壞了,你通過偵錯程式看到的可能是乙個和諧的場景。

稍微跑一下題。這很類似量子力學的「測不准原理」,觀測者的觀測行為干擾了被測量的客體,導致觀測者看到的是乙個干擾後的現象。

關於log輸出

既然斷點和單步不好用。那咋辦捏?乙個替代方案是輸出log日誌。它可以有效減輕斷點和單步所導致的(針對競態條件的)***。

◇傳統log機制的問題

傳統的log輸出主要是列印到螢幕或者輸出到檔案。對於c++而言,標準庫內置的類和函式(比如cout、printf、fputs)可能會有執行緒安全的問題(和編譯器的具體實現有關)。尤其是標準流類庫(iostream)的八個全域性物件,更是要小心慎用。輕則輸出的log文字混雜,重則導致程式崩潰。

鑑於上述原因,應該盡量使用第三方執行緒庫內置的log機制來搞定log輸出功能。比如ace內建的ace_log_msg等。

◇log函式要短小精悍

很多情況下,我們會包裝乙個公用的函式來實現log輸出功能。然後在該函式內部呼叫執行緒庫的log類/函式。為了不影響執行緒的競態條件,這個log函式要盡可能簡單輕便:不要涉及太多雜七雜八的瑣事、千萬別進行耗時的操作、盡量不操作一些全域性的變數。

◇log的***

不過捏,即使log函式再短小精悍,也還是有可能影響競態條件(畢竟log也有開銷,也要消耗cpu時間)。

萬一競態條件受到log的影響,那就比較棘手了。我以前就碰到過這種情況:加了log,程式沒有問題;去掉log,程式隨機崩潰。這種情況一般有兩種可能:要麼是log功能本身有問題,要麼是程式的競態條件非常敏感(連log的開銷都會有影響)。

這時候你能依靠的就只有肉眼和人腦了。先把相關的**和文件仔細看上幾遍(最好再找其他有經驗的人一起code review),然後大家一起開動腦筋使勁琢磨。

關於debug版本和release版本

c++程式經常有debug版本和release版本的區別。有些時候,這也會導致一些多執行緒的問題。

由於debug版本包含了一些除錯資訊、啟用了某些除錯機制(比如assert巨集)。所以就可能影響到多執行緒的競爭狀態。在倒霉的時候,會碰上debug版本工作正常,release版本程式隨機崩潰。要避免這種情況,可以考慮下面兩個辦法:

◇放棄使用debug版本

你可以乾脆放棄使用debug版本。在這種情況下,你需要考慮把諸如assert之類除錯相關的巨集替換成自己的一套巨集,使得在非debug版本下也可以生效。

◇兩種版本同步測試

使用此方法,程式設計師平時自測可以使用debug版本,但是測試人員日常測試的必須是release版本。具體的操作步驟可以利用每日構建來輔助進行(每日構建的介紹參見「這裡 」)。一定要避免:在平時僅僅搞debug版本的測試,等到發布前夕再製作release版本。這種做法是非常危險的!

關於測試的機器(硬體)

說乙個親身經歷、印象深刻的事情。

當年用ace開發跨平台程式的時候,公司內的的開發環境和測試環境都是單cpu的機器。因為當時多核的機器還沒有面世,多cpu的機器又挺貴,公司沒捨得花錢配置。

軟體開發完之後,測試人員經過幾輪回歸測試,也沒發現太大問題。但是拿到客戶的環境中執行,卻經常會隨機性崩潰。因為不能在客戶環境中debug,自己的環境又死活沒問題,開發組的幾個人只好充分發揮肉眼和人腦的功能(盯著**和設計文件猛想)。經過n長時間,差點把腦袋想破,最後才意識到客戶的機器是多cpu的。然後趕緊從其它部門借了一台多cpu機器,裝上軟體除錯,最後查出是乙個第三方庫有問題。此事過後,我立即想出各種法子,去申請了幾台多cpu機器給測試人員用。

由於上述的前車之鑑,所以我強烈建議:如果是開發多執行緒的應用程式,盡量給每乙個程式設計人員和測試人員都配置多核/多cpu的機器。畢竟現在多核機器已經很普及了,即使多cpu的機器,**也還湊合。實在沒必要為了省那點小錢而引入開發風險(不光會浪費開發/測試人員的時間,還可能增加實施和維護的成本)。

另外,可能有同學會問「超執行緒的機器如何捏?」關於多cpu 、多核 、超執行緒 三者之間的差異,有興趣的同學可以看「這裡 」。我個人感覺超執行緒不如多核與多cpu爽。

C 多執行緒除錯和測試的注意事項

在上次的帖子 聊了c 多執行緒的跨平台問題,後來感覺意猶未盡。今天順便說一下開發c 多執行緒應用程式時,有關除錯和測試的一些注意事項。下面這些注意事項主要是針對c 不過有些對於其它的語言也適用。關於設定斷點和單步執行 很多同學非常依賴於偵錯程式的斷點功能和單步功能。這在單執行緒情況下倒還好 不過有些...

多執行緒使用注意事項

在開發過程中經常使用到多執行緒。對ios的多執行緒有很多不解的地方,這裡暫且記錄下來。具體原因有待以後慢慢研究。void scanloop pool release 以asyncudpsocket物件為引數的處理函式,如下所示 void processipscanwithsocket asyncud...

多執行緒程式設計注意事項

1 明確目的,為什麼要使用多執行緒?如果是由於單執行緒讀寫或者網路訪問 例如http訪問網際網路 的瓶頸,可以考慮使用執行緒池。如果是對不同的資源 例如socket連線 進行管理,可以考慮多個執行緒。2 執行緒使用中要注意,如何控制線程的排程和阻塞,例如利用事件的觸發來控制線程的排程和阻塞,也有用訊...