1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
|
---
title: Atomics
slug: Web/JavaScript/Reference/Global_Objects/Atomics
tags:
- JavaScript
translation_of: Web/JavaScript/Reference/Global_Objects/Atomics
---
<p>{{JSRef}}</p>
<p><strong><code>Atomics</code></strong> 对象提供了一组静态方法对 {{jsxref("SharedArrayBuffer")}} 和 {{jsxref("ArrayBuffer")}} 对象进行原子操作。</p>
<h2 id="描述">描述</h2>
<p>这些原子操作属于 <code>Atomics</code> 模块。与一般的全局对象不同,<code>Atomics</code> 不是构造函数,因此不能使用 <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/new">new</a> 操作符调用,也不能将其当作函数直接调用。<code>Atomics</code> 的所有属性和方法都是静态的(与 {{jsxref("Math")}} 对象一样)。</p>
<h3 id="原子操作">原子操作</h3>
<p>多个共享内存的线程能够同时读写同一位置上的数据。原子操作会确保正在读或写的数据的值是符合预期的,即下一个原子操作一定会在上一个原子操作结束后才会开始,其操作过程不会中断。</p>
<h3 id="等待和通知">等待和通知</h3>
<p><code>wait()</code> 和 <code>notify()</code> 方法采用的是 Linux 上的 futexes 模型(“快速用户空间互斥量”),可以让进程一直等待直到某个特定的条件为真,主要用于实现阻塞。</p>
<h2 id="方法">方法</h2>
<dl>
<dt>{{jsxref("Atomics.add()")}}</dt>
<dd>将指定位置上的数组元素与给定的值相加,并返回相加前该元素的值。</dd>
<dt><strong>{{jsxref("Atomics.and()")}}</strong></dt>
<dd>将指定位置上的数组元素与给定的值相与,并返回与操作前该元素的值。</dd>
<dt>{{jsxref("Atomics.compareExchange()")}}</dt>
<dd>如果数组中指定的元素与给定的值相等,则将其更新为新的值,并返回该元素原先的值。</dd>
<dt>{{jsxref("Atomics.exchange()")}}</dt>
<dd>将数组中指定的元素更新为给定的值,并返回该元素更新前的值。</dd>
<dt>{{jsxref("Atomics.isLockFree()", "Atomics.isLockFree(size)")}}</dt>
<dd>可以用来检测当前系统是否支持硬件级的原子操作。对于指定大小的数组,如果当前系统支持硬件级的原子操作,则返回 <code>true</code>;否则就意味着对于该数组,<code>Atomics</code> 对象中的各原子操作都只能用锁来实现。此函数面向的是技术专家。</dd>
<dt>{{jsxref("Atomics.load()")}}</dt>
<dd>返回数组中指定元素的值。</dd>
<dt>{{jsxref("Atomics.notify()")}}</dt>
<dd>唤醒等待队列中正在数组指定位置的元素上等待的线程。返回值为成功唤醒的线程数量。</dd>
<dt>{{jsxref("Atomics.or()")}}</dt>
<dd>将指定位置上的数组元素与给定的值相或,并返回或操作前该元素的值。</dd>
<dt>{{jsxref("Atomics.store()")}}</dt>
<dd>将数组中指定的元素设置为给定的值,并返回<strong>该值</strong>。</dd>
<dt>{{jsxref("Atomics.sub()")}}</dt>
<dd>将指定位置上的数组元素与给定的值相减,并返回相减前该元素的值。</dd>
<dt>{{jsxref("Atomics.wait()")}}</dt>
<dd>检测数组中某个指定位置上的值是否仍然是给定值,是则保持挂起直到被唤醒或超时。返回值为 "<code>ok</code>"、"<code>not-equal</code>" 或 "<code>time-out</code>"。调用时,如果当前线程不允许阻塞,则会抛出异常(大多数浏览器都不允许在主线程中调用 <code>wait()</code>)。</dd>
<dt>{{jsxref("Atomics.xor()")}}</dt>
<dd>将指定位置上的数组元素与给定的值相异或,并返回异或操作前该元素的值。</dd>
</dl>
<h2 id="示例">示例</h2>
<h3 id="使用_Atomics">使用 Atomics</h3>
<pre class="brush: js notranslate">const sab = new SharedArrayBuffer(1024);
const ta = new Uint8Array(sab);
ta[0];
// 0
ta[0] = 5;
// 5
Atomics.add(ta, 0, 12);
// 5
Atomics.load(ta, 0);
// 17 ✅
// 12 ❌
Atomics.and(ta, 0, 1);
// 17
Atomics.load(ta, 0);
// 1
Atomics.compareExchange(ta, 0, 5, 12);
Atomics.load(ta, 0); // 12
Atomics.exchange(ta, 0, 12);
Atomics.load(ta, 0); // 12
Atomics.isLockFree(1); // true
Atomics.isLockFree(2); // true
Atomics.isLockFree(3); // false
Atomics.isLockFree(4); // true
Atomics.or(ta, 0, 1);
Atomics.load(ta, 0); // 5
Atomics.store(ta, 0, 12); // 12
Atomics.sub(ta, 0, 2);
Atomics.load(ta, 0); // 3
Atomics.xor(ta, 0, 1);
Atomics.load(ta, 0); // 4
</pre>
<h3 id="Waiting_和_notifiying">Waiting 和 notifiying</h3>
<p>给定一个共享的 <code>Int32Array</code>:</p>
<pre class="brush: js notranslate">const sab = new SharedArrayBuffer(1024);
const int32 = new Int32Array(sab);
</pre>
<p>读取线程正在休眠并位置0上等待。只要该位置应为0,它就不会继续。但是,一旦写入线程存储了新值,写入线程将通知它并返回新值(123)。</p>
<pre class="brush: js notranslate">Atomics.wait(int32, 0, 0);
console.log(int32[0]); // 123
</pre>
<p>写入线程存储一个新值并再写入完成时通知等待线程:</p>
<pre class="brush: js notranslate">console.log(int32[0]); // 0;
Atomics.store(int32, 0, 123);
Atomics.notify(int32, 0, 1);</pre>
<h2 id="规范">规范</h2>
<table class="standard-table">
<thead>
<tr>
<th scope="col">Specification</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{SpecName('ESDraft', '#sec-atomics-object', 'Atomics')}}</td>
</tr>
</tbody>
</table>
<h2 id="浏览器兼容性">浏览器兼容性</h2>
<p>{{Compat("javascript.builtins.Atomics")}}</p>
<h2 id="相关链接">相关链接</h2>
<ul>
<li>{{jsxref("ArrayBuffer")}}</li>
<li><a href="/zh-CN/docs/Web/JavaScript/Typed_arrays">JavaScript typed arrays</a></li>
<li><a href="/zh-CN/docs/Web/API/Web_Workers_API">Web Workers</a></li>
<li><a href="https://github.com/lars-t-hansen/parlib-simple">parlib-simple </a>– a simple library providing synchronization and work distribution abstractions.</li>
<li><a href="https://github.com/tc39/ecmascript_sharedmem/blob/master/TUTORIAL.md">Shared Memory – a brief tutorial</a></li>
<li><a href="https://hacks.mozilla.org/2016/05/a-taste-of-javascripts-new-parallel-primitives/">A Taste of JavaScript’s New Parallel Primitives – Mozilla Hacks</a></li>
</ul>
|