併發編程Bug的源頭:可見性、原子性和有序性問題。 「CPU、記憶體、磁碟之間的速度差異」 - 為了能同時執行多個任務,CPU 發展出時間片輪轉、多核等 - CPU 要從記憶體中讀數據太慢了,所以給自己設置了緩存 - CPU 讀磁碟更慢了,所以可以讓該線程阻塞 ...
一、根本原因
「CPU、記憶體、磁碟之間的速度差異」
- 為了能同時執行多個任務,CPU 發展出時間片輪轉、多核等
- CPU 要從記憶體中讀數據太慢了,所以給自己設置了緩存
- CPU 讀磁碟更慢了,所以可以讓該線程阻塞
二、直接原因
緩存導致的可見性問題
CPU 把要處理的數據載入到自己的緩存中,處理完了放回自己的緩存。
另一個 CPU 同樣的處理,就導致可能看不到上一個 CPU 處理的結果。
線程切換帶來的原子性問題
程式中的一行代碼往往不是一條 CPU 指令。
線程切換的時候,可能會在一個代碼執行的中間地方切換。
編譯優化帶來的有序性問題
優化會為了更高效的利用 CPU 緩存,將代碼指令重排。
這個重排的過程會導致看似沒問題的代碼,多線程出現邏輯問題。
三、為啥要遇到這些問題
- 為了提高程式的性能
- 引入了新方法,就要處理這個方法對應的問題
- 可以說是硬體工程師和操作系統工程師為了提高執行效率,給軟體工程師帶來的麻煩
接下來的幾篇文章會介紹如何解決上面提到的「可見性、原子性、有序性」的問題。