Filas, Concorrencia e Threads em Clojure
⏳ Filas, Concorrência e Threads em Clojure
Seção intitulada “⏳ Filas, Concorrência e Threads em Clojure”Este capítulo complementa o uso de clojure.lang.PersistentQueue e aborda conceitos de concorrência, controle de fluxo, tratamento de erros e interoperação com Java para trabalhar com Thread.
🚦 Implementar um limite à fila usando if
Seção intitulada “🚦 Implementar um limite à fila usando if”Para controlar o tamanho da fila, podemos usar if e a função count:
(def limite 5)
(defn adicionar-na-fila [fila elemento] (if (< (count fila) limite) (conj fila elemento) (do (println "Fila cheia!") fila)))
(def fila clojure.lang.PersistentQueue/EMPTY)(def fila (adicionar-na-fila fila :a));; => #queue [:a]🔢 Verificar o tamanho da fila com count
Seção intitulada “🔢 Verificar o tamanho da fila com count”A função count funciona com qualquer coleção Clojure:
(count fila);; => 1❗ Tratar erros não previstos com ex-info
Seção intitulada “❗ Tratar erros não previstos com ex-info”Para lançar exceções com informações contextuais:
(throw (ex-info "Erro de processamento" {:codigo 500 :detalhe "Falha no servidor"}))Para capturar:
(try (throw (ex-info "Algo deu errado" {:motivo :desconhecido})) (catch clojure.lang.ExceptionInfo e (println "Erro capturado:" (.getMessage e)) (println "Dados:" (ex-data e))))🧵 Criar uma Thread com compatibilidade Java
Seção intitulada “🧵 Criar uma Thread com compatibilidade Java”Clojure é 100% compatível com Java. Podemos usar Thread diretamente:
(def t (Thread. (fn [] (println "Rodando em outra thread..."))))Ou mais idiomaticamente com proxy ou reify para implementar interfaces:
(def t (Thread. (reify Runnable (run [_] (println "Executando via reify!")))))▶️ Iniciar uma Thread com o método start
Seção intitulada “▶️ Iniciar uma Thread com o método start”Uma vez criada a Thread, iniciamos com start:
(.start t)⚠️ Problemas com símbolos globais e concorrência
Seção intitulada “⚠️ Problemas com símbolos globais e concorrência”Evitar estados globais mutáveis é essencial. Quando várias threads acessam e modificam um mesmo símbolo global, surgem problemas clássicos de race condition e inconsistência:
(def contador (atom 0))
(defn incrementar [] (dotimes [_ 1000] (swap! contador inc)))
;; Executando em paralelo(doseq [_ (range 10)] (.start (Thread. incrementar)))
;; Resultado correto com atom, mas com var mutável normal daria erroPor isso, Clojure favorece o uso de:
atom(atualização atômica simples)ref+dosync(transações coordenadas)agent(atualização assíncrona)future,promise,core.async, entre outras