Os Mutexes são necessários em javascript?

104

Eu vi este link: Implementing Mutual Exclusion in JavaScript . Por outro lado, eu li que não há tópicos em javascript, mas o que exatamente isso significa?

Quando os eventos ocorrem, onde no código eles podem interromper?

E se não houver threads em JS, preciso usar mutexes em JS ou não?

Especificamente, eu estou querendo saber sobre os efeitos do uso de funções chamadas por setTimeout()e XmlHttpRequesté onreadystatechangeem variáveis globalmente acessíveis.

Ovesh
fonte
1
Não, nenhum mutex ou qualquer outra ferramenta de controle de concorrência em javascript. Veja Por que nenhuma ferramenta de controle de concorrência em javascript .
Uzair Farooq

Respostas:

101

Javascript é definido como uma linguagem reentrante , o que significa que não há threading exposto ao usuário, pode haver threads na implementação. Funções como setTimeout()callbacks assíncronos precisam esperar o mecanismo de script dormir antes de serem executados.

Isso significa que tudo o que acontece em um evento deve ser concluído antes que o próximo evento seja processado.

Dito isso, você pode precisar de um mutex se seu código fizer algo em que espera que um valor não mude entre o momento em que o evento assíncrono foi disparado e o momento em que o retorno de chamada foi chamado.

Por exemplo, se você tiver uma estrutura de dados onde você clica em um botão e ele envia um XmlHttpRequest que chama um retorno de chamada, a estrutura de dados muda de forma destrutiva, e você tem outro botão que muda a mesma estrutura de dados diretamente, entre quando o evento foi disparado e quando o retorno de chamada foi executado, o usuário poderia ter clicado e atualizado a estrutura de dados antes do retorno de chamada, o que poderia então perder o valor.

Embora você possa criar uma condição de corrida como essa, é muito fácil evitar isso em seu código, pois cada função será atômica. Seria muito trabalhoso e alguns padrões de codificação estranhos para criar a condição de corrida de fato.

William
fonte
14
Não é difícil criar essa condição de corrida: por exemplo, eu tenho um evento "onkeyup" em um campo, que dispara uma chamada ajax para um banco de dados para obter alguns valores. A digitação rápida de dados pode levar facilmente a resultados fora de ordem.
thomasb de
19

As respostas a esta pergunta estão um pouco desatualizadas, embora corretas no momento em que foram dadas. E ainda correto se olhar para um aplicativo javascript do lado do cliente que NÃO usa webworkers.

Artigos sobre web-workers:
multithreading em javascript usando webworkers
Mozilla em webworkers

Isso mostra claramente que o javascript via web-workers possui recursos de multithreading. Quanto à questão, são necessários mutexes em javascript? Eu não tenho certeza disso. Mas este post stackoverflow parece relevante:
Exclusão mútua para N threads assíncronos

gorilatron
fonte
3
explosão do passado, mas eu encontrei a necessidade de mutexes quando várias guias acessam o mesmo armazenamento local
PSP
3
WebWorkers não afetam a reentrada porque eles não compartilham nenhum estado variável e apenas se comunicam com o thread principal passando mensagens, que acionam eventos.
Alnitak
9

Como @william aponta,

você pode precisar de um mutex se seu código fizer algo onde espera que um valor não mude entre o momento em que o evento assíncrono foi disparado e o momento em que o retorno de chamada foi chamado.

Isso pode ser generalizado ainda mais - se o seu código fizer algo onde espera o controle exclusivo de um recurso até que uma solicitação assíncrona seja resolvida, você pode precisar de um mutex.

Um exemplo simples é onde você tem um botão que dispara uma chamada ajax para criar um registro no back end. Você pode precisar de um pouco de código para protegê-lo de usuários felizes que cliquem e, assim, criem vários registros. existem várias abordagens para esse problema (por exemplo, desabilitar o botão, habilitar com sucesso no Ajax). Você também pode usar um cadeado simples:

var save_lock = false;
$('#save_button').click(function(){
    if(!save_lock){
        //lock
        save_lock=true;
        $.ajax({
            success:function()
                //unlock
                save_lock = false;  
            }
        });
    }
}

Não tenho certeza se essa é a melhor abordagem e gostaria de ver como outras pessoas lidam com a exclusão mútua em javascript, mas, pelo que sei, é um mutex simples e é prático.

Alzclarke
fonte
4
Eu dificilmente chamaria isso de mutex, pelo menos não no sentido tradicional, porque você não tem dois threads em execução no contexto de um único bloco a qualquer momento.
Ovesh de
10
um mutex é simplesmente um algoritmo que ajuda a 'evitar o uso simultâneo de um recurso comum'. Embora o multithreading crie a necessidade de mutexes, não há nada na definição que diga que um mutex é específico para a situação que você descreve.
alzclarke
1
Você está correto sobre a definição formal de mutex. Mas isso dificilmente é o que as pessoas pensam quando falam sobre mutexes no mundo real.
Ovesh
Isso não está funcionando conforme o esperado. Infelizmente, cliques repetidos ainda dispararão a chamada de ajax. Alguma outra ideia?
Mohammed Shareef C
1
Com certeza, isso deve ser agrupado em a whilewith setTimeoutou a setIntervalwith clearIntervalapós n falhas, para que você tenha uma nova tentativa e lógica de tempo limite. Deixar como está significa que você simplesmente ignorará o código que está bloqueado. O tratamento externo com mutexes e objetos compartilhados é tão importante quanto as próprias implementações.
MrMesees
6

JavaScript é single threaded ... embora o Chrome possa ser uma nova besta (eu acho que também é single threaded, mas cada guia tem seu próprio thread JavaScript ... Eu não olhei em detalhes, então não me cite há).

No entanto, uma coisa com a qual você precisa se preocupar é como seu JavaScript lidará com várias solicitações de ajax que retornam não na mesma ordem em que você as enviou. Portanto, tudo o que você realmente precisa se preocupar é ter certeza de que suas chamadas ajax sejam tratadas de uma forma que não pisem umas nas outras se os resultados vierem em uma ordem diferente da que você enviou.

Isso vale para tempos limite também ...

Quando o JavaScript crescer multithreading, então talvez se preocupe com mutexes e coisas do gênero ....

Mike Stone
fonte
4

Sim, mutexes podem ser requeridos em Javascript ao acessar recursos que são compartilhados entre guias / janelas, como localStorage .

Por exemplo, se um usuário tem duas guias abertas, um código simples como o seguinte não é seguro:

function appendToList(item) {
    var list = localStorage["myKey"];
    if (list) {
        list += "," + item;
    }
    else {
        list = item;
    }
    localStorage["myKey"] = list;
}

Entre o momento em que o item localStorage é 'obtido' e 'definido', outra guia pode ter modificado o valor. Geralmente é improvável, mas possível - você precisa julgar por si mesmo a probabilidade e o risco associados a qualquer contenção em suas circunstâncias particulares.

Consulte os seguintes artigos para obter mais detalhes:

decates
fonte
2

JavaScript, a linguagem , pode ser tão multithread quanto você quiser, mas os embeddings do navegador do mecanismo javascript só executam um retorno de chamada (onload, onfocus, <script>, etc ...) por vez (por guia, presumivelmente). A sugestão de William de usar um Mutex para alterações entre o registro e o recebimento de um retorno de chamada não deve ser tomada muito literalmente por causa disso, já que você não gostaria de bloquear o retorno de chamada intermediário, pois o retorno de chamada que irá desbloqueá-lo será bloqueado atrás do retorno de chamada atual ! (Uau, o inglês é péssimo para falar sobre threading.) Nesse caso, você provavelmente deseja fazer algo semelhante a reenviar o evento atual se um sinalizador for definido, literalmente ou com algo como setTimeout ().

Se você estiver usando um embedding diferente de JS e que executa vários threads ao mesmo tempo, pode ficar um pouco mais arriscado, mas devido à maneira como o JS pode usar callbacks com tanta facilidade e bloqueia objetos no acesso à propriedade, o bloqueio explícito não é tão necessário . No entanto, eu ficaria surpreso se uma incorporação projetada para código geral (por exemplo, script de jogo) que usava multiencadeamento também não fornecesse alguns primitivos de bloqueio explícitos.

Desculpa pelo texto imenso!

Simon Buchan
fonte
0

Os eventos são sinalizados, mas a execução do JavaScript ainda é de thread único.

Meu entendimento é que quando o evento é sinalizado, o mecanismo para o que está executando no momento para executar o manipulador de eventos. Depois que o manipulador é concluído, a execução do script é retomada. Se o manipulador de eventos alterou algumas variáveis ​​compartilhadas, o código retomado verá essas alterações aparecendo "do nada".

Se você deseja "proteger" os dados compartilhados, um sinalizador booleano simples deve ser suficiente.

Constantin
fonte