Em um programa multithreaded (ou .Net Java), eu posso assumir que a cópia de uma variável é atômica?

StackOverflow https://stackoverflow.com/questions/473111

Pergunta

Eu estava preocupado com as condições de corrida em um aplicativo que eu estou projetando, quando eu estava pensando sobre esta questão.

Vamos dizer que eu tenho uma grande variedade ou coleção de algum tipo que é gerido por um componente do meu programa, vamos chamada que Monitor de componente. Seu trabalho é verificar regularmente se a coleção é "sujo", i. e. mudou recentemente, e em caso afirmativo, escrever um instantâneo para o disco (este é o ponto de verificação da aplicação no caso de um acidente ocorre) e marcá-lo como limpo novamente.

Outros componentes de um mesmo programa, em execução em um segmento diferente, chamar métodos do monitor para adicionar dados ou modificar dados na matriz / coleção. Esses métodos marcam a coleção como sujo.

Agora, os métodos que alteram executado em tópicos dos outros componentes, certo? E se eu não tenho tanta sorte, que poderia ser chamado enquanto o instantâneo é gravado em disco, os dados de mudança que já foi escrito, definir o sinalizador sujo e desactiva fios do monitor apenas depois disso, sem ter guardado as alterações (-lo já passado o elemento foi quando mudou). Então, eu tenho uma coleção suja que está marcado como limpo.

Por um tempo, eu pensei que eu poderia talvez resolver o problema fazendo uma cópia temporária da coleção, marcá-lo limpo e, em seguida, ir e serializar a cópia. Mas será que a cópia seja atômica, i. e. Posso ter certeza de que a cobrança não vai mudar enquanto estou copiando-o?

Enquanto isso, eu acho que encontrei as melhores soluções, como

  • definindo um sinalizador de bloqueio antes de iniciar a gravação em disco e tornar os dados métodos alterando esperar até que a bandeira é unset
  • tem os métodos que alteram dados gravar em um "fila de mudança" em vez de diretamente para a recolha e tem a linha que faz o processo de escrita de disco que fila

e eu acho que a bandeira de bloqueio pode ser o melhor caminho a percorrer. Mas eu ainda estou curioso: é copiar uma variável atômica

?

Acompanhamento : Talvez isso deve ir em uma questão de sua própria, mas na verdade é muito o mesmo. De acordo com as respostas abaixo, a minha abordagem "bandeira bloqueio" pode também não trabalho, certo? Porque o método de alteração de dados pode verificar o sinalizador de bloqueio enquanto ele está sendo definido para o valor "bloqueado" e decidir que não está bloqueado. Então eu preciso de uma construção especial como um mutex se eu realmente quero fazer isso direito, correto?


Kudos para Erickson por sua muito útil resposta no meu follow-up. Eu realmente deveria ter feito isso duas perguntas para que eu pudesse ter aceitado duas respostas. Por favor, votá-lo, também.

Foi útil?

Solução

No. Por exemplo, longos variáveis ??em Java não são atômicas em máquinas de 32 bits.

Além disso, há um problema "thread cache" - a menos que a variável é volátil ou dentro sincronizado bloco, outro segmento pode não ver a mudança de valor variável. Isto é verdade para todos os tipos de variáveis, não apenas o tempo.

Leia aqui: http://gee.cs.oswego.edu/dl /cpj/jmm.html , especialmente "atomicidade" e "visibilidade" parágrafos.

Outras dicas

Não, não é atômica. Veja este pergunta por que eo que fazer sobre isso.

Dê uma olhada em java.util.concurrent.atomic -. pode haver algumas coisas boas lá você pode usar

Você precisa se preocupar com a visibilidade de mudanças para outros segmentos quando trabalham na JVM. Em geral, você deve fazer suas atribuições dentro de um bloco synchronized, ou a variável deve ser volatile, ou você deve usar um invólucro variável do pacote java.util.concurrent.atomic.

No entanto, no seu caso, parece que você tem apenas um segmento que sempre limpa o "sujo" bandeira-a linha que persiste os dados. Se for esse o caso, limpar o sinalizador antes escrever os dados. Se outros tópicos defini-lo enquanto você está escrevendo os dados, ele vai ficar definido até que a gravação próxima agendada. Eu usaria um AtomicBoolean, para dar a sua persistência fio atomicidade entre a verificação da bandeira e limpá-lo, como este:

private final AtomicBoolean dirty = new AtomicBoolean();

/**
 * Any method that modifies the data structure should set the dirty flag.
 */
public void modify() {
  /* Modify the data first. */
  ...
  /* Set the flag afterward. */
  dirty.set(true);
}

private class Persister extends Thread {
  public void run() {
    while (!Thread.interrupted()) {
      if (dirty.getAndSet(false)) {
        /* The dirty flag was set; this thread cleared it 
         * and should now persist the data. */
         ...
      }
    }
  }
}

A definição de um 32-bit (pelo menos em .NET) é atômica, mas faz você não é bom. Você tem que lê-lo para saber se ele está bloqueado, de modo que você pode lê-lo, e depois da leitura, alguém lê-lo antes de defini-lo, de modo que dois tópicos acabam dentro o código "protegido" . Este é exatamente o que objetos de sincronização real (como a classe .NET monitor) são para. Você poderia usar um Interlocked para verificar e incrementar a variável bloqueio também.

Veja também: está acessando uma variável em C # um atômica operação?

depende muito do hardware e do JVM você está executando

Em algum hardware e algumas JVMs algumas cópias será atômica mas é mais seguro para assumir este não é o caso até mesmo um número inteiro simples de atribuição inteiro pode se traduzir em quatro instruções de máquina em hardware x856.

corda e cópias de matriz pode envolver uma sequência de milhares de instruções e é possível que dois tópicos para atualizar simultaniously.

Pelo que entendi, nem sempre.

Int32 será, um Int64 não será em um 32 bit sytem como ele precisa de 2 x 32 bit. Portanto, ele não se encaixa em um 32 célula bit.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top