困擾了程序員半個月的Bug,被解決之後,程序員:“就這?”

話說,我們公司有個項目在二十多天前就已經到了合同約定的驗收時間了,但是因爲項目當時上得比較急,所以,在驗收時間到達的時候,我們老闆跟客戶好說歹說才又爭取了一個星期的延期。

當初我認爲一個星期的時間是綽綽有餘的,並且,我也在規定的時間內完成了我自己所承擔的所有開發內容,但是,誰知道一個星期後,這個項目還是沒有達到驗收標準,原因就是我同事負責的一個功能運行得始終有問題。

簡單地說,同事負責開發的這個功能是需要同時向多個機械設備發送指令讓其按照規則運轉,注意,是同步運轉!而問題也出在這裡!

同事代碼出現問題的特徵就是,他寫的代碼並不是百分百有效,有時候發送指令效果是正確的,即幾個機械設備在同時運轉,但有時候就只有那麼一兩個機械設備在運轉。

遇到這個問題,我首先想到的就是讓同事去調試,看看指令是否正確發到了每個機械設備上了。結果,這半個月,我同事當着我的面調試了好幾次,發現指令發的都是對的!

開始,我還沒發現問題,後來當問題解決了以後,我回想我每次看同事調試的時候的場景,我才意識到,我同事每次在調試的時候,設備都能正常運轉原因!

因爲總是解決不掉問題,客戶那邊也擺爛了,隨我們搞,但是卻沒有給我們好臉色,這讓我有點被殃及池魚的感覺!

後來,直到有一天,我被客戶那邊一個PM給說了一頓後,情緒有點失控,所以,我就下定決心好好找一找我同事代碼中的茬!

因爲同事需要操作的機械設備邏輯比較複雜,所以,我看着也有些迷迷糊糊的,所以一時半會兒也找不到問題出在哪!

最後,我決定使用排除法,把同事代碼中所有跟問題無關的代碼全部註釋掉,只留下關鍵代碼的方式去找問題,我不知道這方法有沒有用,但我還是決定死馬當活馬醫!

誰知道,就是因爲我的這一個操作,找到了導致代碼不能正確運行的原因!

因爲代碼比較複雜,我在註釋同事寫的跟問題無關的代碼的時候,一邊問同事一邊註釋,結果當我瞥到一個字段的時候,我竟然發現這個字段竟然是靜態的!

這個字段我其實一直很熟悉,它代表的是需要操作的設備的索引!我腦袋當時就炸了,因爲這個靜態字段所在的實體類是需要實例化的!我一直沒注意,以爲它是類的實例字段。

首先,在一個需要實例化的實體類中放一個靜態字段本身就不太合適,除非這個靜態字段的值是個常量(勉強能解釋吧)。

但是,這個靜態字段表示的是設備的索引,在實例化後是要被賦值的,因此,靜態字段表示設備的索引,顯然是有問題的。

這麼一想,我突然就豁然開朗了!

示例

示例

前面說了,同事代碼的問題點就是每次向設備發送指令的時候,就一兩個設備有響應,在調試代碼的時候,卻又是正常執行的,即所有設備都能響應。

問題恰恰就出在這個靜態字段上!

因爲,向設備發送指令是使用線程異步發送的,而每次發送指令,都需要實例化一個發送指令的實體,並且給這個實體中的靜態字段即表示設備索引的字段賦值。

但是,因爲是異步發送的,表示設備索引的那個字段又是靜態的,那麼完全有可能在發送指令的時候,設備索引又被重新賦了值,從而導致指令沒有向正確的設備發送。

比如說,假設給設備索引的這個字段賦值爲0,當代碼還沒執行到發送指令的代碼段時,其他線程又給設備索引賦值爲了1,而當兩個線程都走到了發送指令這塊代碼時,此時,設備索引這個字段的值也就只能是1了,造成的結果就是索引爲0的設備沒有收到指令,而索引爲1的設備,則收到了兩次指令!

而當同一個設備收到相同指令時,新的指令會覆蓋舊的指令,因此,從感官上,不會有任何不正常的情況。

這個道理說通了,那麼就可以解釋問題了,因爲在極端情況下,是會出現只有一臺設備收到指令的情況,這種情況,代碼執行得很快的話,概率是非常高的!

另外,也可以解釋爲什麼有時候只有一兩個設備收到了指令,因爲只要設備索引這個靜態字段值在沒被下一個索引的值覆蓋前執行了指令代碼,那麼,相關設備就會收到指令信號!

至於調試的時候,爲什麼都能執行正確呢?那就更簡單了,因爲同事在代碼中下了斷點,阻礙了代碼的正常執行,也就會導致設備索引這個字段是按照順序被賦值和發送指令的,因此就不會出問題!

結語

我嘆了一口氣,困擾了我們,準確的說困擾了我同事半個月時間的問題,總算是找到了!我也是無語了,就問同事爲什麼會把設備索引這個字段寫成靜態的。

結果同事想了半天,也說不出個所以然來!最後我也不想說啥,只要項目能夠順利驗收就好!

在解決問題後,我總結了下,同事代碼出現問題有很多必然性,我到現在都搞不清楚爲什麼在一個實體類裡面用靜態字段,還需要賦值!

最後,半個月啊!就這?