iOS-GCD-Read Write lock

Gordon Feng
程式愛好者
Published in
Feb 17, 2022
Either write, or read, you can’t do both at the same time, right?

Let’s talk about data racing first, should we?

Data racing

什麼情況下,我們會遇到Data racing?

若同一塊記憶體同時被兩條A, B不同的Thread寫入,就會造成無法預測這塊記憶體最後寫入的是A資料或B資料,下面是我們一個簡單的Sample code。

Data racing example.

在上述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會依序執行下面三步驟:

  1. 當.barrier送進Queue中,會暫停執行所有.barrier插入後Queue所接受到的非同步任務。
  2. 待.barrier插入前的所有的任務完成後,執行.barrier本身的任務(在本範例為寫入任務)。
  3. 當.barrier任務完成後,解除暫停,Queue繼續執行待執行任務。

值得注意的是:因為同步執行本身就是有順序的執行任務,因此,在同步執行下設定.barrier是沒有意義的。而基於DispatchQueue.global並不會受到.barrier效果影響,實作Read Write lock時,請使用自己創造的Queue。

執行一次上述程式碼你會發現,在寫入任務完成之前,不會有任何讀取的動作執行,在寫入任務執行完畢後,才會執行讀取任務。

--

--