JavaScript Avançado

O que Todo Dev Deve Saber sobre SharedArrayBuffer e Atomics: Memória Compartilhada entre Workers

6 min de leitura

O que Todo Dev Deve Saber sobre SharedArrayBuffer e Atomics: Memória Compartilhada entre Workers

SharedArrayBuffer: O Que É e Por Que Usar SharedArrayBuffer é um objeto JavaScript que permite compartilhar um buffer de memória entre múltiplos workers (threads). Diferentemente dos Web Workers comuns, que comunicam-se por cópia de dados (postMessage), o SharedArrayBuffer oferece acesso direto à mesma região de memória, eliminando overhead de serialização. Isso é essencial em aplicações CPU-intensivas, processamento de vídeo, simulações 3D e análise de grandes datasets em tempo real. A principal vantagem é o desempenho: múltiplos workers podem ler e modificar os mesmos dados simultaneamente sem cópias custosas. Porém, com grande poder vem grande responsabilidade — você herda problemas clássicos de programação paralela como race conditions e deadlocks. É aqui que entra a API Atomics. Configurando SharedArrayBuffer Criação Básica Para criar um SharedArrayBuffer, você instancia-o como um TypedArray. Diferentemente de ArrayBuffer comum, este é compartilhável entre contextos: Restrições de Segurança Por questões de segurança (mitigação de Spectre), navegadores modernos exigem headers específicos. O servidor deve retornar: Atomics: Operações Thread-Safe A

<h2>SharedArrayBuffer: O Que É e Por Que Usar</h2>

<p>SharedArrayBuffer é um objeto JavaScript que permite compartilhar um buffer de memória entre múltiplos workers (threads). Diferentemente dos Web Workers comuns, que comunicam-se por cópia de dados (postMessage), o SharedArrayBuffer oferece acesso direto à mesma região de memória, eliminando overhead de serialização. Isso é essencial em aplicações CPU-intensivas, processamento de vídeo, simulações 3D e análise de grandes datasets em tempo real.</p>

<p>A principal vantagem é o desempenho: múltiplos workers podem ler e modificar os mesmos dados simultaneamente sem cópias custosas. Porém, com grande poder vem grande responsabilidade — você herda problemas clássicos de programação paralela como race conditions e deadlocks. É aqui que entra a API Atomics.</p>

<h2>Configurando SharedArrayBuffer</h2>

<h3>Criação Básica</h3>

<p>Para criar um SharedArrayBuffer, você instancia-o como um TypedArray. Diferentemente de ArrayBuffer comum, este é compartilhável entre contextos:</p>

<pre><code class="language-javascript">// main.js

const sharedBuffer = new SharedArrayBuffer(32); // 32 bytes

const sharedArray = new Int32Array(sharedBuffer);

// Inicializa dados

sharedArray[0] = 100;

// Cria e passa para worker

const worker = new Worker(&#039;worker.js&#039;);

worker.postMessage({ sharedBuffer });</code></pre>

<pre><code class="language-javascript">// worker.js

self.onmessage = (event) =&gt; {

const { sharedBuffer } = event.data;

const sharedArray = new Int32Array(sharedBuffer);

console.log(&#039;Valor inicial:&#039;, sharedArray[0]); // 100

sharedArray[0] = 200; // Modifica diretamente na memória compartilhada

};</code></pre>

<h3>Restrições de Segurança</h3>

<p>Por questões de segurança (mitigação de Spectre), navegadores modernos exigem headers específicos. O servidor deve retornar:</p>

<pre><code>Cross-Origin-Opener-Policy: same-origin

Cross-Origin-Embedder-Policy: require-corp</code></pre>

<h2>Atomics: Operações Thread-Safe</h2>

<p>A API Atomics fornece métodos que garantem operações indivisíveis (atômicas) sobre SharedArrayBuffer, prevenindo race conditions. Os principais são <code>load</code>, <code>store</code>, <code>compareExchange</code>, <code>wait</code> e <code>notify</code>.</p>

<h3>Operações Básicas Atômicas</h3>

<pre><code class="language-javascript">// main.js

const sharedBuffer = new SharedArrayBuffer(8);

const sharedArray = new Int32Array(sharedBuffer);

// Escrita atômica

Atomics.store(sharedArray, 0, 42);

// Leitura atômica

const value = Atomics.load(sharedArray, 0);

console.log(value); // 42

// Compare-and-swap (atualiza se valor == esperado)

const wasUpdated = Atomics.compareExchange(sharedArray, 0, 42, 100);

console.log(wasUpdated); // 42 (valor anterior)

console.log(Atomics.load(sharedArray, 0)); // 100</code></pre>

<p>Sem Atomics, leituras/escritas simples (<code>sharedArray[0] = 42</code>) não são garantidas como thread-safe em todas as arquiteturas. Atomics garante que a operação completa atomicamente antes de qualquer outra instrução interferir.</p>

<h3>Wait e Notify: Sincronização Entre Threads</h3>

<p>O padrão mais poderoso é usar <code>wait()</code> e <code>notify()</code> para sincronização produtor-consumidor:</p>

<pre><code class="language-javascript">// main.js (produtor)

const sharedBuffer = new SharedArrayBuffer(4);

const sharedArray = new Int32Array(sharedBuffer);

const worker = new Worker(&#039;worker.js&#039;);

worker.postMessage({ sharedBuffer });

// Simula produção

setTimeout(() =&gt; {

Atomics.store(sharedArray, 0, 42);

Atomics.notify(sharedArray, 0); // Acorda workers aguardando

}, 1000);</code></pre>

<pre><code class="language-javascript">// worker.js (consumidor)

self.onmessage = (event) =&gt; {

const { sharedBuffer } = event.data;

const sharedArray = new Int32Array(sharedBuffer);

console.log(&#039;Esperando dados...&#039;);

Atomics.wait(sharedArray, 0, 0); // Bloqueia até notify ou timeout

const value = Atomics.load(sharedArray, 0);

console.log(&#039;Recebi:&#039;, value); // 42

};</code></pre>

<p><code>Atomics.wait()</code> só funciona em Workers, nunca na thread principal (evita congelamento da UI). Quando <code>notify()</code> é chamado, todos os workers aguardando aquele índice são acordados.</p>

<h2>Caso de Uso Prático: Pipeline de Processamento</h2>

<p>Imagine processar frames de vídeo com múltiplos workers. Cada worker processa um quadro diferentes enquanto compartilham estado de progresso:</p>

<pre><code class="language-javascript">// main.js

const FRAME_COUNT = 100;

const sharedBuffer = new SharedArrayBuffer(4 * 3); // 3 Int32s

const state = new Int32Array(sharedBuffer); // [framesProcessadas, erros, pausado]

// Inicia 4 workers

const workers = Array.from({ length: 4 }, () =&gt; {

const w = new Worker(&#039;frame-processor.js&#039;);

w.postMessage({ sharedBuffer, totalFrames: FRAME_COUNT });

return w;

});

// Monitor progress

setInterval(() =&gt; {

const processed = Atomics.load(state, 0);

console.log(Progresso: ${processed}/${FRAME_COUNT});

}, 500);</code></pre>

<pre><code class="language-javascript">// frame-processor.js

self.onmessage = (event) =&gt; {

const { sharedBuffer, totalFrames } = event.data;

const state = new Int32Array(sharedBuffer);

for (let i = 0; i &lt; totalFrames; i++) {

// Processa frame

processFrame(i);

// Incremento atômico do contador

Atomics.add(state, 0, 1);

}

};

function processFrame(index) {

// Simulação: processamento pesado

let sum = 0;

for (let i = 0; i &lt; 1000000; i++) sum += Math.sqrt(i);

}</code></pre>

<h2>Conclusão</h2>

<p>SharedArrayBuffer e Atomics são ferramentas poderosas para paralelismo real em JavaScript. Primeira aprendizagem: <strong>SharedArrayBuffer elimina cópias de dados</strong>, permitindo que múltiplos workers acessem a mesma memória. Segunda: <strong>Atomics garante operações thread-safe</strong> e fornece primitivas de sincronização (wait/notify). Terceira: <strong>use esse padrão apenas quando necessário</strong> — overhead de sincronização pode degradar performance em casos triviais. Domine esses conceitos e você dominará computação paralela em JavaScript.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer" target="_blank" rel="noopener noreferrer">MDN: SharedArrayBuffer</a></li>

<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics" target="_blank" rel="noopener noreferrer">MDN: Atomics API</a></li>

<li><a href="https://html.spec.whatwg.org/multipage/workers.html" target="_blank" rel="noopener noreferrer">WHATWG: HTML Living Standard - Workers</a></li>

<li><a href="https://developer.chrome.com/docs/workbox/worker-performance/" target="_blank" rel="noopener noreferrer">Web Workers Performance Guide - Google Developers</a></li>

<li><a href="https://javascript.info/web-workers" target="_blank" rel="noopener noreferrer">JavaScript.info: Web Workers</a></li>

</ul>

Comentários

Mais em JavaScript Avançado

Boas Práticas de Design Patterns em JavaScript: Observer, Mediator e Command para Times Ágeis
Boas Práticas de Design Patterns em JavaScript: Observer, Mediator e Command para Times Ágeis

Observer: Reatividade em Tempo Real O padrão Observer implementa um sistema d...

Arquitetura Interna do Node.js: libuv, Thread Pool e Event Loop na Prática
Arquitetura Interna do Node.js: libuv, Thread Pool e Event Loop na Prática

O Coração do Node.js: Entendendo a Arquitetura Node.js é famoso por sua capac...

Dominando React Internals: Reconciler, Fiber Architecture e Rendering Phases em Projetos Reais
Dominando React Internals: Reconciler, Fiber Architecture e Rendering Phases em Projetos Reais

Entendendo o Reconciler: O Coração do React O Reconciler é o mecanismo que Re...