我們公司的一位程式設計師同事對我說:“我遇到了乙個非常奇怪的**!聽他這麼說有點緊張,我很好奇**什麼能叫“怪異”。 然後他跟我解釋,他寫了乙個for迴圈,然後用for迴圈的索引來得到陣列對應下標的值,但是這個**好像沒有效果,明明迴圈終止條件是設定i<=20,但最後發現i的值是21!我一聽到他描述這樣的事情,我立刻產生了興趣。然後我拿他的**除錯了一下,發現真的和他描述的一樣,迴圈終止條件是i<=20,但是i的最後值是21!
*這個問題真的很奇怪,但我喜歡它!但是我看了一會兒他的***,有點不好意思,一時也不確定他的***寫有沒有問題!然而,唯一可以肯定的是,他在for迴圈中使用了非同步執行緒,我想問題仍然出線上程上。
所以,我寫了乙個完全模擬他的寫作風格的演示來測試我的想法。
我寫了乙個 0 到 20 的 for 迴圈,然後直接非同步輸出 i 的值,最後我發現這個 i 的值不僅輸出 21,甚至 21 個輸出輸出 i 的一半以上都有 21 的值!
想了一會兒,我想出了問題的原因!
出現此問題的原因是迴圈使用非同步輸出,在併發輸出的情況下,i 的輸出順序不確定,這也破壞了 i 值的原子性,最終可能導致 i 的值與輸出時的預期不同。
打個簡單的比方,因為for迴圈內部使用非同步輸出,所以for迴圈會在很短的時間內執行,而for迴圈執行完之後,就可能執行非同步邏輯了,這時i的值可能是20,所以如果非同步輸出的邏輯是在for迴圈結束後執行的, 那麼這個時候輸出的 i,那麼 i 的值應該是 20,所以,原則上會有大量的輸出 20 個值!
但是,我們也發現 i 的值並不是我們想象的那樣,它不是 20 而是 21,這就把我們帶到了 for 迴圈的執行邏輯。
假設 i 的起始值為 0,結束值為 20,則 for 迴圈的執行邏輯是每次進行迴圈時都會給出 i+1,當它準備進入下乙個迴圈時,會首先判斷 i 是否大於結束值, 如果它大於它,則不會執行。
換句話說,當執行最後乙個迴圈時,for 迴圈仍然給出 i+1,因此如果執行非同步輸出的邏輯,則 i 等於 21!
我們可以用乙個簡單的例子來證明這個推論(這裡沒有複雜的程式設計基礎知識,都是關於驗證的)。
我們可以在 for 迴圈之外定義乙個值為 0 的變數 i,即避免在 for 迴圈時建立變數,讓我們呼叫 for 迴圈之外的輸出,當 for 迴圈結束時(這裡沒有非同步操作),我們輸出 i 的值,你會發現 i 的值是 21 而不是 20!
我們都知道這個問題,但是如果我的同事必須在他的 for 迴圈中使用非同步怎麼辦?解決方法也很簡單,就是直接定義乙個變數來獲取每個迴圈中 i 的值,並且這個變數相對於 for 迴圈是原子的,然後,在非同步時間內使用這個變數而不是直接使用 i,就這樣,問題就解決了!
如何?你學會了嗎?