Pergunta

Eu estou tentando aprender Clojure da API e documentação disponível no site. Eu sou um pouco incerto sobre o armazenamento mutável em Clojure e eu quero ter certeza que meu entendimento está correto. Por favor, deixe-me saber se há alguma idéia que eu comecei errado.

Edit:. Estou atualizando isso como eu receber comentários sobre a sua exactidão


Disclaimer: Toda esta informação é informal e potencialmente errado. Não use este post para ganhar uma compreensão de como Clojure funciona.


Vars sempre conter uma ligação de raiz e, possivelmente, um por thread de ligação. Eles são comparáveis ??às variáveis ??regulares em linguagens imperativas e não são adequados para a partilha de informações entre os segmentos. (graças Arthur Ulfeldt)

Refs são locais compartilhados entre threads que suportam transações atômicas que podem alterar o estado de qualquer número de refs em uma única transação. As transações são confirmadas ao sair expressões de sincronização (dosync) e os conflitos são resolvidos automaticamente com STM mágica (reversões, filas, esperas, etc.)

Agentes são locais que permitem que informações sejam de forma assíncrona compartilhada entre segmentos com sobrecarga mínima, enviando funções ação independente de alterar o estado do agente. Agentes são devolvidos imediatamente e são, portanto, sem bloqueio, embora o valor de um agente não está definido até que uma função despachado foi concluída.

Átomos são locais que podem ser sincronicamente compartilhados entre threads. Eles apoiam a manipulação segura entre diferentes threads.

Aqui está o meu resumo amigável com base em quando usar essas estruturas:

  • Vars são como velhos variáveis ??regulares em linguagens imperativas. (Evitar, quando possível)
  • Os átomos são como Vars mas com a segurança de compartilhamento de fio que permite a leitura imediata e ambiente seguro. (graças Martin)
  • Um agente é como um Atom, mas em vez de bloquear ele gera um novo segmento para calcular seu valor, apenas os blocos se no meio de mudar um valor, e pode permitir que outros tópicos saber que é atribuição final.
  • Refs são locais que se travam em transações compartilhada. Em vez de fazer o programador decidir o que acontece durante a condições de corrida para cada pedaço de código bloqueado, nós simplesmente iniciar uma transação e deixar Clojure lidar com todas as condições de bloqueio entre as refs em que transação.

Além disso, um conceito relacionado é a função future. Para mim, parece que um objeto futuro pode ser descrito como um agente síncrona, onde o valor não pode ser acessado em tudo até o cálculo é concluído. Ele também pode ser descrito como um átomo de não-bloqueio. São estas concepções precisas do futuro?

Foi útil?

Solução

Parece que você está realmente recebendo Clojure! bom trabalho:)

Vars ter uma "raiz de ligação" visível em todos os tópicos e cada thread individual pode mudar o valor que ele vê com a afetar as outras threads. Se o meu entendimento é uma var correta não pode existir em apenas um segmento com uma ligação que raiz é visível a todos e não pode ser "rebote" até que tenha sido definido com (def ...) pela primeira vez.

Refs estão comprometidos no final do (dosync ...) transação que envolve as mudanças, mas somente quando a transação foi capaz de terminar em um estado consistente.

Outras dicas

Eu acho que sua conclusão sobre Átomos está errado:

Os átomos são como Vars mas com a segurança de compartilhamento de rosca que bloqueia até que o valor foi alterado

Os átomos são alterados com swap! ou de baixo nível com compare-and-set!. Isso nunca blocos nada. swap! funciona como uma transação com apenas um ref:

  1. o valor antigo é retirado do átomo e armazenados segmento local
  2. a função é aplicada ao valor antigo para gerar um novo valor
  3. se este consegue comparar-e-set é chamado com o valor antigo e novo; somente se o valor do átomo não foi alterado por qualquer outra thread (ainda é igual ao valor de idade), o novo valor é escrito, caso contrário, a operação recomeça no (1) até sucede eventualmente.

Eu encontrei dois problemas com a sua pergunta.

Você diz:

Se um agente é acessado enquanto uma acção está a ocorrer, em seguida, o valor não é devolvido até que a acção tenha terminado

http://clojure.org/agents diz:

o estado de um agente é sempre imediatamente disponível para leitura por qualquer segmento

i. você nunca tem que esperar para obter o valor de um agente (I assumir o valor alterado por uma ação é proxy e mudou atomicamente).

O código para o deref-método de um Agent é assim (SVN revisão 1382):

public Object deref() throws Exception{
    if(errors != null)
    {
        throw new Exception("Agent has errors", (Exception) RT.first(errors));
    }
return state;

}

No bloqueio está envolvido.

Além disso, eu não entendo o que você quer dizer (em sua seção Ref) por

As transações estão comprometidos em chamadas para deref

As transações são cometidos quando todas as ações do bloco dosync foram concluídos, sem exceções foram jogados e nada tem causado a transação a ser repetida. Acho deref não tem nada a ver com isso, mas talvez eu entenda mal o seu ponto.

Martin tem razão quando diz que os átomos de operação é reiniciado em 1. até que seja bem-sucedido, eventualmente. É também chamado de rotação de espera. Enquanto ele é nota realmente bloqueio em um bloqueio do segmento que fez a operação é bloqueado até que a operação for bem-sucedido por isso é uma operação de bloqueio e não uma operação assíncrona.

Também sobre Futures, Clojure 1.1 adicionou abstrações de promessas e futuros. Uma promessa é uma construção de sincronização que pode ser usada para fornecer um valor de um segmento para outro. Até que o valor tenha sido entregue, qualquer tentativa de excluir a referência a promessa irá bloquear.

(def a-promise (promise))
(deliver a-promise :fred)

Futures representam computações assíncronas. Eles são uma maneira de obter o código seja executado em outro segmento, e obter o resultado.

(def f (future (some-sexp)))
(deref f) ; blocks the thread that derefs f until value is available

Vars nem sempre têm uma ligação de raiz. É legal para criar uma var sem uma ligação usando

(def x)

ou

(declare x)

A tentativa de avaliar x antes que ele tenha um valor resultará em

Var user/x is unbound.
[Thrown class java.lang.IllegalStateException]
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top