Operações Atômicas e Não Atômicas em Ruby

Operações Atômicas e Não Atômicas em Ruby
Photo by Noor Sethi / Unsplash

Operações Atômicas e Não Atômicas em Ruby: Uma Visão Detalhada

Quando se trata de programação concorrente em Ruby, é crucial entender a diferença entre operações atômicas e não atômicas. Esta compreensão é fundamental para escrever código seguro em ambientes multithread.

O que são Operações Atômicas?

Uma operação atômica é aquela que é realizada como uma única unidade de trabalho sem a possibilidade de ser interrompida. Em um ambiente multithread, isso significa que, uma vez iniciada, a operação não pode ser interrompida por outras threads até ser concluída. Isso é crucial para evitar condições de corrida e inconsistências de dados.

Exemplos de Operações Atômicas em Ruby

Ruby fornece várias operações atômicas através da biblioteca concurrent-ruby. Vamos ver alguns exemplos:

  1. Atomic Reference:

    require 'concurrent'
    
    atomic_count = Concurrent::AtomicReference.new(0)
    
    # Incremento atômico
    atomic_count.update { |value| value + 1 }
    
  2. Atomic Fixnum:

    atomic_fixnum = Concurrent::AtomicFixnum.new
    
    # Operações atômicas de incremento e decremento
    atomic_fixnum.increment
    atomic_fixnum.decrement
    

O que são Operações Não Atômicas?

Operações não atômicas são aquelas que podem ser interrompidas ou interferidas por outras operações. Em um ambiente multithread, isso pode levar a condições de corrida, onde o resultado da operação depende da ordem não determinística das execuções das threads.

Exemplos de Operações Não Atômicas

Vamos considerar um exemplo simples de incremento de uma variável compartilhada, que é uma operação não atômica:

counter = 0

# Thread 1
Thread.new do
  1000.times { counter += 1 }
end

# Thread 2
Thread.new do
  1000.times { counter += 1 }
end

# Espera as threads terminarem
Thread.list.each { |t| t.join unless t == Thread.current }

# Resultado pode não ser 2000 devido à condição de corrida
puts counter

Neste exemplo, counter += 1 é uma operação não atômica, pois envolve a leitura e a escrita da variável counter. Duas threads estão modificando counter simultaneamente, o que pode levar a resultados inesperados.

Como Garantir Operações Atômicas?

Para garantir que operações sejam atômicas em Ruby, você pode usar mutexes ou estruturas de dados atômicas fornecidas por bibliotecas como concurrent-ruby.

Usando Mutex para Operações Atômicas

Um Mutex é um mecanismo de sincronização que pode ser usado para garantir que apenas uma thread possa executar um bloco de código por vez.

mutex = Mutex.new
counter = 0

# Thread 1
Thread.new do
  1000.times do
    mutex.synchronize { counter += 1 }
  end
end

# Thread 2
Thread.new do
  1000.times do
    mutex.synchronize { counter += 1 }
  end
end

# Espera as threads terminarem
Thread.list.each { |t| t.join unless t == Thread.current }

# Resultado será 2000
puts counter

Conclusão

Entender a diferença entre operações atômicas e não atômicas é crucial para a programação concorrente em Ruby. Operações atômicas garantem consistência e segurança em ambientes multithread, enquanto operações não atômicas podem levar a condições de corrida e resultados inesperados. Utilizar mutexes ou estruturas de dados atômicas são maneiras eficazes de garantir a atomicidade das operações.

Read more