Introdução à memória compartilhada em JavaScript
Memoria compartilhada é um recurso avançado do JavaScript, que os encadeamentos (partes executadas simultaneamente de um processo) podem aproveitar. Compartilhando os meios de memória não tendo o problema de passar dados atualizados entre os tópicos e todos os threads podem acessar e atualizar os mesmos dados na memória compartilhada.
Isso não soa adorável? Bem, quase. Neste post, vamos ver como usar a memória compartilhada em JavaScript e como decidir se é isso que você realmente quer fazer.
Vantagens e desvantagens da memória compartilhada
Nós usamos trabalhadores da web para criar tópicos em JavaScript. A API Web Workers nos permite criar encadeamentos de trabalho que podem ser usados para executar código em segundo plano para que o thread principal esteja livre para continuar sua execução, possivelmente processando eventos de UI, garantindo que nenhum congelamento de UI.
Tópicos de trabalho executar simultaneamente com o thread principal e uns aos outros. Essa execução simultânea de diferentes partes de uma tarefa economiza tempo. Você termina mais rápido, mas também tem seu próprio conjunto de problemas.
Certificando-se de que cada segmento Obtém os recursos necessários e se comunica uns com os outros em tempo hábil é uma tarefa em si, onde um acidente pode resultar em um resultado surpreendente. Ou se um thread está mudando dados e outro está lendo ao mesmo tempo, o que você acha que o outro segmento vai ver? Os dados atualizados ou antigos?
No entanto, os web workers não são tão fáceis de estragar. Durante a comunicação através do uso de mensagens, os dados que enviam um ao outro são não original, mas uma cópia, ou seja, eles não compartilhar os mesmos dados. Eles passar cópias de dados entre si quando necessário.
Mas compartilhar é cuidar e vários segmentos também podem precisar examinar os mesmos dados ao mesmo tempo e alterá-los. assim, banir a partilha é um grande não-não. É aqui que o SharedArrayBuffer
objeto entra em cena. Isso nos deixará compartilhar dados binários entre vários segmentos.
o SharedArrayBuffer
objeto
Em vez de passar as cópias de dados entre os threads, nós passar cópias do SharedArrayBuffer
objeto. UMA SharedArrayBuffer
objeto aponta para a memória onde os dados são salvos.
Então, mesmo quando as cópias de SharedArrayBuffer
são passados entre threads, eles todos ainda apontarão para a mesma memória onde os dados originais são salvos. Os fios, assim, podem visualizar e atualizar os dados nessa mesma memória.
Trabalhadores da Web sem memoria compartilhada
Para ver como um trabalhador da Web trabalha sem usar a memória compartilhada, criar um segmento de trabalho e passar alguns dados para ele.
o index.html
arquivo contém o roteiro principal dentro de um tag, como você pode ver abaixo:
const w = new Worker ('worker.js'); var n = 9; w.postMessage (n);
o worker.js
arquivo carrega o roteiro do trabalhador:
onmessage = (e) => console.group ('[trabalhador]'); console.log ('Dados recebidos do thread principal:% i', e.data); console.groupEnd ();
Usando o código acima, obtemos o seguinte saída no console:
[trabalhador] Dados recebidos do thread principal: 9
Você pode ler o meu post mencionado acima em trabalhadores da web para obter a explicação completa do código dos snippets acima.
Por enquanto, lembre-se de que os dados são enviado de um lado para outro entre tópicos usando o postar mensagem()
método. Os dados são recebido do outro lado pelo mensagem
manipulador de eventos, como o valor do evento dados
propriedade.
Agora, se nós mudar os dados Ele aparecerá atualizado no final do recebimento? Vamos ver:
const w = new Worker ('worker.js'); var n = 9; w.postMessage (n); n = 1;
Como esperado, o dados tem não foi atualizado:
[trabalhador] Dados recebidos do thread principal: 9
Por que seria assim? Está apenas um clone enviado para o trabalhador a partir do roteiro principal.
Trabalhadores da Web com memoria compartilhada
Agora vamos use o SharedArrayBuffer
objeto no mesmo exemplo. Nós podemos criar um novo SharedArrayBuffer
exemplo por usando o Novo
palavra chave. O construtor usa um parâmetro; uma valor do comprimento em bytes, especificando o tamanho do buffer.
const w = new Worker ('worker.js'); buff = new SharedArrayBuffer (1); var arr = new Int8Array (buff); / * dados de configuração * / arr [0] = 9; / * enviando o buffer (cópia) para worker * / w.postMessage (buff);
Note que um SharedArrayBuffer
objeto representa apenas uma área de memória compartilhada. Para ver e alterar os dados binários, precisamos usar uma estrutura de dados apropriada TypedArray
ou um Exibição de dados
objeto).
No index.html
arquivo acima, um novo SharedArrayBuffer
é criado, com apenas um byte de comprimento. Então, um novo Int8Array
, que é um tipo de TypedArray
objetos, é usado para definir os dados para “9” no espaço de bytes fornecido.
onmessage = (e) => var arr = novo Int8Array (e.data); console.group ('[worker]'); console.log ('Dados recebidos do encadeamento principal:% i', arr [0]); console.groupEnd ();
Int8Array
também é usado no trabalhador, para ver os dados no buffer.
o valor esperado aparece no console do thread de trabalho, que é exatamente o que nós queríamos:
[trabalhador] Dados recebidos do thread principal: 9
Agora vamos atualizar os dados no segmento principal para ver se a mudança é refletida no trabalhador.
const w = new Worker ('worker.js'), buff = novo SharedArrayBuffer (1); var arr = new Int8Array (buff); / * dados de configuração * / arr [0] = 9; / * enviando o buffer (cópia) para worker * / w.postMessage (buff); / * alterando os dados * / arr [0] = 1;
E, como você pode ver abaixo, a atualização reflete dentro do trabalhador!
[trabalhador] Dados recebidos do thread principal: 1
Mas o código também precisa trabalhar ao contrário: quando o valor no trabalhador muda no início, também precisa ser atualizado quando é impresso a partir do segmento principal.
Neste caso, nosso código se parece com isto:
onmessage = (e) => var arr = novo Int8Array (e.data); console.group ('[worker]'); console.log ('Dados recebidos do encadeamento principal:% i', arr [0]); console.groupEnd (); / * alterando os dados * / arr [0] = 7; / * postagem no segmento principal * / postMessage (");
o os dados são alterados no trabalhador e um mensagem vazia é postada no tópico principal sinalização de que os dados no buffer foram alterados e estão prontos para o thread principal ser gerado.
const w = new Worker ('worker.js'), buff = novo SharedArrayBuffer (1); var arr = new Int8Array (buff); / * dados de configuração * / arr [0] = 9; / * enviando o buffer (cópia) para worker * / w.postMessage (buff); / * alterando os dados * / arr [0] = 1; / * imprimindo os dados após o trabalhador ter alterado * / w.onmessage = (e) => console.group ('[main]'); console.log ('Dados atualizados recebidos do thread de trabalho:% i', arr [0]); console.groupEnd ();
E isso também funciona! Os dados no buffer são os mesmos que os dados dentro do trabalhador.
[trabalhador] Dados recebidos do thread principal: 1 [main] Dados atualizados recebidos do thread de trabalho: 7
O valor que aparece atualizado em ambos os casos; os threads principal e de trabalho estão exibindo e alterando os mesmos dados.
Palavras finais
Como mencionei anteriormente, usando memória compartilhada em JavaScript não é sem desvantagens. Cabe aos desenvolvedores garantir que o seqüência de execução acontece como previsto e não há dois segmentos estão correndo para obter os mesmos dados, porque ninguém sabe quem vai levar o troféu.
Se você está mais interessado em memória compartilhada, dê uma olhada na documentação do Atomics
objeto. o Objeto Atomics pode ajudá-lo com algumas das dificuldades, reduzindo a natureza imprevisível da leitura / escrita da memória compartilhada.