Cri*_*ìna 12 javascript specifications ecmascript-2017
的ECMAScript规范定义了原子能对象在部分24.4.
在所有的全球对象中,这对我来说更加模糊,因为在我没有阅读其规范之前我不知道它的存在,而且谷歌也没有多少引用它(或者名称太过通用和一切都被淹没?).
根据其官方定义
Atomics对象提供在共享内存阵列单元上不可分割地(原子地)操作的函数以及允许代理等待和分派原始事件的函数
因此它具有一个对象的形状,有许多方法来处理低级内存并调节对它的访问.它的公共界面也让我想到了.但是这种对象对最终用户的实际使用是什么?为什么公开?是否有一些可用的例子?
谢谢
小智 14
Atomics用于同步共享内存的WebWorkers.它们导致对SharedArrayBuffer的内存访问以线程安全的方式完成.共享内存使多线程更有用,因为:
例:
var arr = new SharedArrayBuffer(1024);
// send a reference to the memory to any number of webworkers
workers.forEach(worker => worker.postMessage(arr));
// Normally, simultaneous access to the memory from multiple threads
// (where at least one access is a write)
// is not safe, but the Atomics methods are thread-safe.
// This adds 2 to element 0 of arr.
Atomics.add(arr, 0, 2)
Run Code Online (Sandbox Code Playgroud)
之前在主要浏览器上启用了SharedArrayBuffer,但在Spectre事件发生后,它被禁用,因为共享内存允许实现纳秒精度计时器,这允许利用幽灵.
为了确保安全,浏览器需要为每个域运行页面单独的进程.Chrome在版本67中开始执行此操作,并在版本68中重新启用了共享内存.
如果你有一些复杂的计算,你可能需要WebWorkers,以便你的主脚本可以继续他的工作,而重要的事情是并行完成的.
Atomics解决的问题是WebVorkers如何在彼此之间进行通信(简单,快速和可靠).您可以在这里阅读有关ArrayBuffer,SharedArrayBuffer,Atomics以及如何将它们用于您的好处的信息.
如果出现以下情况,你不应该为此烦恼:
您可能需要它:
WebAssembly
或webgl
希望优化性能,则需要这样做感谢Pavlo Mur和Simon Paris为您提供答案!
原子操作是“全有或全无”的一组较小的操作。
让我们来看看
let i=0;
i++
Run Code Online (Sandbox Code Playgroud)
i++
实际上是通过 3 个步骤进行评估的
i
值i
1如果有 2 个线程执行相同的操作会发生什么?它们都可以读取相同的值1
并在完全相同的时间增加它。
是的!JavaScript 确实是单线程,但浏览器/节点今天允许并行使用多个 JavaScript 运行时(工作线程、Web 工作线程)。
Chrome 和 Node(基于 v8)为每个线程创建Isolate,它们都在自己的context
.
而罐子的唯一途径share memory
是通过ArrayBuffer
/SharedArrayBuffer
使用 node > =10 运行(您可能需要--experimental_worker
标志)
node example.js
const { isMainThread, Worker, workerData } = require('worker_threads');
if (isMainThread) {
// main thread, create shared memory to share between threads
const shm = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT);
process.on('exit', () => {
// print final counter
const res = new Int32Array(shm);
console.log(res[0]); // expected 5 * 500,000 = 2,500,000
});
Array(5).fill(null).map(() => new Worker(__filename, { workerData: shm }));
} else {
// worker thread, iteratres 500k and doing i++
const arr = new Int32Array(workerData);
for (let i = 0; i < 500000; i++) {
arr[i]++;
}
}
Run Code Online (Sandbox Code Playgroud)
输出可能是 2,500,000
但我们不知道,在大多数情况下它不会是 2.5M,实际上,您两次获得相同输出的机会非常低,作为程序员我们肯定不会就像我们不知道它会如何结束的代码。
这是竞争条件的一个示例,其中 n 个线程相互竞争并且不以任何方式同步。
Atomic
操作来了,它允许我们从头到尾进行算术运算。
让我们稍微改变一下程序,现在运行:
const { isMainThread, Worker, workerData } = require('worker_threads');
if (isMainThread) {
const shm = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT);
process.on('exit', () => {
const res = new Int32Array(shm);
console.log(res[0]); // expected 5 * 500,000 = 2,500,000
});
Array(5).fill(null).map(() => new Worker(__filename, { workerData: shm }));
} else {
const arr = new Int32Array(workerData);
for (let i = 0; i < 500000; i++) {
Atomics.add(arr, 0, 1);
}
}
Run Code Online (Sandbox Code Playgroud)
现在输出将永远是 2,500,000
有时,我们希望一个操作只能同时被 1 个线程访问,让我们看看下一个类
class Mutex {
/**
*
* @param {Mutex} mutex
* @param {Int32Array} resource
* @param {number} onceFlagCell
* @param {(done)=>void} cb
*/
static once(mutex, resource, onceFlagCell, cb) {
if (Atomics.load(resource, onceFlagCell) === 1) {
return;
}
mutex.lock();
// maybe someone already flagged it
if (Atomics.load(resource, onceFlagCell) === 1) {
mutex.unlock();
return;
}
cb(() => {
Atomics.store(resource, onceFlagCell, 1);
mutex.unlock();
});
}
/**
*
* @param {Int32Array} resource
* @param {number} cell
*/
constructor(resource, cell) {
this.resource = resource;
this.cell = cell;
this.lockAcquired = false;
}
/**
* locks the mutex
*/
lock() {
if (this.lockAcquired) {
console.warn('you already acquired the lock you stupid');
return;
}
const { resource, cell } = this;
while (true) {
// lock is already acquired, wait
if (Atomics.load(resource, cell) > 0) {
while ('ok' !== Atomics.wait(resource, cell, 0));
}
const countOfAcquiresBeforeMe = Atomics.add(resource, cell, 1);
// someone was faster than me, try again later
if (countOfAcquiresBeforeMe >= 1) {
Atomics.sub(resource, cell, 1);
continue;
}
this.lockAcquired = true;
return;
}
}
/**
* unlocks the mutex
*/
unlock() {
if (!this.lockAcquired) {
console.warn('you didn\'t acquire the lock you stupid');
return;
}
Atomics.sub(this.resource, this.cell, 1);
Atomics.notify(this.resource, this.cell, 1);
this.lockAcquired = false;
}
}
Run Code Online (Sandbox Code Playgroud)
现在,您需要SharedArrayBuffer
在所有线程之间分配和共享它们,并看到每次只有 1 个线程进入critical section
运行节点 > 10
node --experimental_worker example.js
const { isMainThread, Worker, workerData, threadId } = require('worker_threads');
const { promisify } = require('util');
const doSomethingFakeThatTakesTimeAndShouldBeAtomic = promisify(setTimeout);
if (isMainThread) {
const shm = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT);
Array(5).fill(null).map(() => new Worker(__filename, { workerData: shm }));
} else {
(async () => {
const arr = new Int32Array(workerData);
const mutex = new Mutex(arr, 0);
mutex.lock();
console.log(`[${threadId}] ${new Date().toISOString()}`);
await doSomethingFakeThatTakesTimeAndShouldBeAtomic(1000);
mutex.unlock();
})();
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
753 次 |
最近记录: |