iOS-GCD-Read Write lock
Let’s talk about data racing first, should we?
Data racing
什麼情況下,我們會遇到Data racing?
若同一塊記憶體同時被兩條A, B不同的Thread寫入,就會造成無法預測這塊記憶體最後寫入的是A資料或B資料,下面是我們一個簡單的Sample code。
在上述Code中,我們將1–1000的數字透過不同的Thread加入一個陣列中,執行程式碼後,每次寫入都會開啟一條Thread執行,在這一千次的執行中,有不小的機率會造成不同的Thread同時對Array進行寫入而出現圖片中的Error。
Thread safe
關於上述的Data racing,起因既然是不同Thread同時寫入同一塊記憶體造成,最簡單的方式就是當我們執行寫入任務時,都使用同一條Thread依序進行寫入,就不會有Data racing的情況發生了。
上述程式碼,我們在這物件內設定一個專門用來寫入的Queue,並在寫入時一律使用該queue執行寫入任務。
Read Write Lock
但若單純只使用上述Thread safe的方式,我們會遇到一個問題:
若我在短時間內同時執行大量的讀取跟寫入,我要如何確保我在寫入的同時,不要執行任何讀取,等到我完成寫入的動作後,再執行讀取動作?
這時候我們就可以使用DispatchQueue的flags設定,裡面的.barrier參數。
.barrier會依序執行下面三步驟:
- 當.barrier送進Queue中,會暫停執行所有.barrier插入後Queue所接受到的非同步任務。
- 待.barrier插入前的所有的任務完成後,執行.barrier本身的任務(在本範例為寫入任務)。
- 當.barrier任務完成後,解除暫停,Queue繼續執行待執行任務。
值得注意的是:因為同步執行本身就是有順序的執行任務,因此,在同步執行下設定.barrier是沒有意義的。而基於DispatchQueue.global並不會受到.barrier效果影響,實作Read Write lock時,請使用自己創造的Queue。
執行一次上述程式碼你會發現,在寫入任務完成之前,不會有任何讀取的動作執行,在寫入任務執行完畢後,才會執行讀取任務。