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: SharedArrayBuffer
slug: Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer
translation_of: Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer
tags:
- Класс
- JavaScript
- Разделяемая память
- SharedArrayBuffer
- TypedArrays
---
<div>{{JSRef}}</div>
<p>Объект <strong><code>SharedArrayBuffer</code></strong> подобен ArrayBuffer, то есть это буфер фиксированной длины, использующийся для хранения любых бинарных данных. Главное отличие <code>SharedArrayBuffer</code> от <code>ArrayBuffer</code> заключается в том, что он используется для создания разделяемой области памяти. В отличие от <code>ArrayBuffer</code> <code>SharedArrayBuffer</code> не может быть откреплён от соответствующей ему области памяти.</p>
<h2 id="Описание">Описание</h2>
<h3 id="Выделение_и_совместное_использование_памяти">Выделение и совместное использование памяти</h3>
<p>Для совместного использования памяти с помощью объекта {{jsxref("SharedArrayBuffer")}} между одним агентом в кластере и другим (агентом может быть как основная программа страницы сайта, так и один из её веб-воркеров) используются <code><a href="/ru/docs/Web/API/Worker/postMessage">postMessage</a></code> и <a href="/ru/docs/Web/API/Web_Workers_API/Structured_clone_algorithm">алгоритм структурированного клонирования</a>.</p>
<p>Алгоритм структурированного клонирования принимает <code>SharedArrayBuffers</code> и <code>TypedArrays</code>, отображённый в <code>SharedArrayBuffers</code>. В обоих случаях объект <code>SharedArrayBuffer</code> передаётся получателю, что приводит к появлению нового приватного объекта SharedArrayBuffer внутри агента-получателя (так же как для {{jsxref("ArrayBuffer")}}). Оба объекта <code>SharedArrayBuffer</code> ссылаются на один и тот же блок общих данных, и побочный эффект, изменяющий блок данных в одном из агентов, в итоге проявится в другом агенте.</p>
<pre class="brush: js">var sab = new SharedArrayBuffer(1024);
worker.postMessage(sab);
</pre>
<h3 id="Обновление_и_синхронизация_разделяемой_памяти_с_помощью_атомарных_операций">Обновление и синхронизация разделяемой памяти с помощью атомарных операций</h3>
<p>Разделяемую память можно создавать и изменять одновременно в воркерах или основном потоке. В зависимости от системы (ЦПУ, ОС, браузера), распространение изменений по всем контекстам может занять некоторое время. Для синхронизации необходимы {{jsxref("Атомарные", "атомарные", "", 1)}} операции .</p>
<h3 id="API_принимающие_объекты_SharedArrayBuffer">API, принимающие объекты <code>SharedArrayBuffer</code></h3>
<ul>
<li><a href="/en-US/docs/Web/API/WebGLRenderingContext/bufferData" title="Метод WebGLRenderingContext.bufferData() WebGL API инициализирует и создает хранилище данных буферного объекта."><code>WebGLRenderingContext.bufferData()</code></a></li>
<li><a href="/en-US/docs/Web/API/WebGLRenderingContext/bufferSubData" title="Метод WebGLRenderingContext.bufferSubData() WebGL API обновляет подмножество хранилища данных буферного объекта."><code>WebGLRenderingContext.bufferSubData()</code></a></li>
<li><a href="/en-US/docs/Web/API/WebGL2RenderingContext/getBufferSubData" title="Метод WebGL2RenderingContext.getBufferSubData() WebGL 2 API считывает данные из привязанной точки буфера и записывает их в ArrayBuffer или SharedArrayBuffer."><code>WebGL2RenderingContext.getBufferSubData()</code></a></li>
</ul>
<h3 id="Требования_безопасности">Требования безопасности</h3>
<p>Разделяемая память и таймеры высокого разрешения были <a href="https://blog.mozilla.org/security/2018/01/03/mitigations-landing-new-class-timing-attack/">отключены в начале 2018 года</a> из-за атаки <a href="https://ru.wikipedia.org/wiki/Spectre_(%D1%83%D1%8F%D0%B7%D0%B2%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D1%8C)">Spectre</a>. В 2020 году был стандартизирован новый, безопасный подход, чтобы включить разделяемую память обратно. При следовании следующим мерам безопасности <code><a href="/ru/docs/Web/API/Window/postMessage">postMessage()</a> не будет выкидывать исключение для <code>SharedArrayBuffer</code>, и разделяемая память будет доступна в разных потоках.</p>
<p>Основное требование — ваш документ должен находиться в <a href="/en-US/docs/Web/Security/Secure_Contexts">безопасном контексте</a></p>
<p>Для документов верхнего уровня нужно устновить два заголовка, чтобы изолировать ваш сайт от других источников (cross-origin):</p>
<ul>
<li><a href="/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy"><code>Cross-Origin-Opener-Policy</code></a> со значением <code>same-origin</code> (защищает ваш источник от атаки)</li>
<li><a href="/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy"><code>Cross-Origin-Embedder-Policy</code></a> со значением <code>require-corp</code> (защищает жертв от вашего источника)</li>
</ul>
<pre class="brush: plain">Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
</pre>
<p>Чтобы проверить, что изоляция от других источников прошла успешно, протестируйте свойство <code><a href="/en-US/docs/Web/API/WindowOrWorkerGlobalScope/crossOriginIsolated">crossOriginIsolated</a></code>, доступное для контекстов окна и воркера:</p>
<pre class="brush: js">if (crossOriginIsolated) {
// Начни работу с SharedArrayBuffer
} else {
// Сделай что-то другое
}</pre>
<p>Ознакомьтесь с <a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer/Planned_changes">планируемыми изменениями разделяемой памяти</a>, которые начинают внедряться в браузерах (например, в Firefox 79).</p>
<h3 id="Всегда_используйте_оператор_new_для_создания_SharedArrayBuffer">Всегда используйте оператор new для создания SharedArrayBuffer</h3>
<p>Конструкторы <code>SharedArrayBuffer</code> необходимо вызывать с помощью оператора {{jsxref("Operators/new", "new")}}. Вызов конструктора <code>SharedArrayBuffer</code> как функции без указания <code>new</code> вызовет ошибку {{jsxref("TypeError")}}.</p>
<pre class="brush: js example-bad">var sab = SharedArrayBuffer(1024);
// TypeError: вызов встроенного конструктора SharedArrayBuffer
// без new запрещено</pre>
<pre class="brush: js example-good">var sab = new SharedArrayBuffer(1024);</pre>
<h2 id="Конструктор">Конструктор</h2>
<dl>
<dt><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer/SharedArrayBuffer"><code>SharedArrayBuffer()</code></a></dt>
<dd>Создаёт новый объект <code>SharedArrayBuffer</code>.</dd>
</dl>
<h3 id="Свойства">Свойства</h3>
<dl>
<dt>{{jsxref("SharedArrayBuffer.prototype.byteLength")}}</dt>
<dd>Размер буферного массива в байтах. Задаётся при создании массива и не может быть изменён. <strong>Только для чтения.</strong></dd>
</dl>
<h2 id="Методы">Методы</h2>
<dl>
<dt>{{jsxref("SharedArrayBuffer.slice", "SharedArrayBuffer.prototype.slice(begin, end)")}}</dt>
<dd>Возвращает новый <code>SharedArrayBuffer</code>, чьё содержимое — копия байтов изначального <code>SharedArrayBuffer</code> с <code>begin</code> (начала) включительно до <code>end</code> (конца), но не включая его. Если параметры <code>begin</code> или <code>end</code> отрицательны, метод обращается к индексу массива, начиная с конца, а не с начала.</dd>
</dl>
<h2 id="Примеры">Примеры</h2>
<h3 id="Создание_нового_SharedArrayBuffer">Создание нового SharedArrayBuffer</h3>
<pre class="brush: js">var sab = new SharedArrayBuffer(1024);</pre>
<h3 id="Нарезание_SharedArrayBuffer">Нарезание SharedArrayBuffer</h3>
<pre class="brush: js">sab.slice(); // SharedArrayBuffer { byteLength: 1024 }
sab.slice(2); // SharedArrayBuffer { byteLength: 1022 }
sab.slice(-2); // SharedArrayBuffer { byteLength: 2 }
sab.slice(0, 1); // SharedArrayBuffer { byteLength: 1 }</pre>
<h3 id="Использование_в_буфере_WebGL">Использование в буфере WebGL</h3>
<pre class="brush: js">const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, sab, gl.STATIC_DRAW);</pre>
<h2 id="Спецификации">Спецификации</h2>
<table class="standard-table">
<tbody>
<tr>
<th scope="col">Спецификация</th>
</tr>
<tr>
<td>{{SpecName('ESDraft', '#sec-sharedarraybuffer-objects', 'SharedArrayBuffer')}}</td>
</tr>
</tbody>
</table>
<h2 id="Поддержка_браузерами">Поддержка браузерами</h2>
<p>{{Compat("javascript.builtins.SharedArrayBuffer")}}</p>
<h2 id="Смотрите_также">Смотрите также</h2>
<ul>
<li>{{jsxref("Atomics")}}</li>
<li>{{jsxref("ArrayBuffer")}}</li>
<li><a href="/ru/docs/Web/JavaScript/Typed_arrays">Типизированные массивы JavaScript</a></li>
<li><a href="/ru/docs/Web/API/Web_Workers_API">Веб-воркеры</a></li>
<li><a href="https://github.com/lars-t-hansen/parlib-simple">parlib-simple</a> — простая библиотека, предоставляющая синхронизацию и абстракции для распределённых работ.</li>
<li><a href="https://github.com/tc39/ecmascript_sharedmem/blob/master/TUTORIAL.md">Разделяемая память — краткая инструкция</a></li>
<li>
<p><a href="https://hacks.mozilla.org/2016/05/a-taste-of-javascripts-new-parallel-primitives/">Немного о новых примитивах JavaScript для параллелизации работ – Mozilla Hacks</a></p>
</li>
</ul>
|