O que deve ser feito se dois trabalhadores acessarem os mesmos dados ao mesmo tempo?
sobes.tech IA
Resposta da IA
É necessário garantir a segurança no acesso aos dados. Abordagens principais:
-
Bloqueios (Locks): São usados para limitar o acesso às secções críticas do código. Apenas um thread pode possuir o bloqueio num dado momento.
# Exemplo com Mutex em Ruby require 'thread' $shared_data = 0 $mutex = Mutex.new def increment_data $mutex.lock # Aquisição do bloqueio begin # Secção crítica temp = $shared_data sleep(0.01) # Simulação de trabalho $shared_data = temp + 1 ensure $mutex.unlock # Liberação do bloqueio end end threads = [] 10.times do threads << Thread.new { increment_data } end threads.each(&:join) puts $shared_data # Garantido será 10 -
Transações de bases de dados: Se os dados estão armazenados numa base de dados, use transações para operações atómicas. O nível de isolamento das transações determina como as alterações feitas por outras transações são visíveis.
# Exemplo em Rails usando ActiveRecord ActiveRecord::Base.transaction do # Todas as operações dentro do bloco são executadas de forma atómica user = User.find(user_id) user.balance -= amount order = Order.create!(user: user, amount: amount) user.save! # Se algo correr mal, toda a transação é revertida end -
Operações atómicas: Algumas linguagens e bibliotecas fornecem tipos de dados ou operações atómicas, que são garantidamente executadas na sua totalidade, sem possibilidade de interrupção por outros threads.
# Ruby não possui tipos atómicos incorporados para dados arbitrários, # mas algumas operações (por exemplo, atribuição) podem ser atómicas # para tipos simples. Para cenários mais complexos, são necessários bloqueios. -
Revisões / Controlo de versões de dados (Optimistic Concurrency Control): Em vez de bloquear dados, cada registo tem um número de revisão ou marca temporal. Ao atualizar, verifica-se se o registo não mudou desde a sua leitura. Se mudou, a operação reinicia.
# Exemplo com ActiveRecord::Locking::Optimistic # Adicione `lock_version` (inteiro) à sua tabela class Product < ApplicationRecord # ... end product = Product.find(id) # Outro thread atualiza o mesmo produto... begin product.price += 10 product.save! # Gera ActiveRecord::StaleObjectError se lock_version mudou rescue ActiveRecord::StaleObjectError # Tratamento do conflito, por exemplo, recarregar e tentar novamente retry end -
Intermediários / Filas de mensagens: Em vez de acesso direto aos dados, os trabalhadores enviam mensagens a um intermediário (por exemplo, RabbitMQ, Sidekiq com Redis). O processamento das mensagens é feito sequencialmente ou numa ordem controlada, o que reduz a probabilidade de conflitos.
-
Utilização de objetos imutáveis: Se os dados forem imutáveis, o acesso simultâneo a eles é seguro, pois nenhum trabalhador pode alterá-los.
A escolha da abordagem ou combinação de abordagens depende do tipo de dados, dos requisitos de desempenho e da arquitetura da aplicação. Em aplicações web com Ruby on Rails, costuma-se usar transações ActiveRecord e bloqueios otimistas para dados na base de dados, e mutex para recursos em memória. As tarefas em segundo plano (como Sidekiq) geralmente gerenciam mensagens da fila, que por si só garantem uma certa ordenação.