ECMAScript 2018 中增長了 SharedArrayBuffer 和 Atomics ,利用它們能夠實現鎖(Lock),即頁面主線程和 Web Worker 線程間的鎖。javascript
SharedArrayBuffer(如下簡稱爲SAB) 是一個能夠主線程和 Web Worker 線程間共享數據的對象,即同一個 SAB 能夠被多個線程讀寫。html
let sab = new SharedArrayBuffer(1024); worker.postMessage(sab);
SAB 避免了多個線程間爲了傳遞數據而進行數據拷貝,但同時缺引入了經典的數據訪問「衝突」:java
// 線程A let sharedBuffer = new SharedArrayBuffer(1024); let sharedArray = new Int32Array(sab); let worker = new Worker("browser-worker.js"); worker.postMessage(sab); sharedArray[1] = 1; // 線程B onmessage = function (e) { let sharedBuffer = e.data; let sharedArray = new Int32Array(sab); sharedArray[1] = 10; sharedArray[2] = sharedArray[1] + sharedArray[1]; // sharedArray[2] = ? :( }
這時候就須要原子操做。git
Atomics 能夠實現對於 SAB 原子訪問,其包含不少操做。github
咱們下面會用到的:算法
Atomics.store(typedArray, index, value)
:向 SAB 的 index 位置賦值 value。post
Atomics.wait(typedArray, index, value[, timeout])
:驗證 Int32Array 的 index 位置是否爲給定的 value,是則睡眠(阻塞),不然繼續執行。atom
Atomics.wake(typedArray, index, count)
:喚醒 Int32Array 的 index 位置上wait 隊列(Atomics.wait 產生)。線程
Atomics.sub(typedArray, index, value)
:index 位置的值減去 value 並保存到 index 位置,返回原值。code
Atomics.compareExchange(typedArray, index, expectedValue, replacementValue)
:若是 index 位置的值爲 expectedValue,則與 replcementValue 交換,返回原值。
有了上面的 SAB 和 Atomics 就能夠實現一個很「簡單」的線程鎖,
簡單來講,就是利用 SAB 在多個線程間共享控制位,當控制位爲「已鎖」時,則暫停線程,這些操做都依賴 Atomics。
注意:這個算法是 Futex,參考了Futexes Are Tricky。
// lock if ((c = Atomics.compareExchange(SAB, index, 0, 1)) !== 0) { // 不爲0,說明其餘人持鎖 do { // 若是依舊得不到鎖 if (c === 2 || Atomics.compareExchange(SAB, index, 1, 2) != 0) { Atomics.wait(SAB, index, 2); // 暫停 } // 再次嘗試獲取鎖 } while ((c = Atomics.compareExchange(SAB, index, 0, 2)) !== 0) } // unlock let v0 = Atomics.sub(SAB, index, 1); // 此時擁有鎖,狀態爲1或2 if (v0 != 1) { Atomics.store(SAB, index, 0); // 釋放鎖 Atomics.wake(SAB, index, 1); // 喚醒一個 wait 的 }