0Pricing
Clojure Functional Programming & JVM Backend Development · Leçon

Refs, agents et atomes pour gérer l'état

Apprenez à utiliser les primitives de concurrence fondamentales de Clojure pour gérer un état modifiable en toute sécurité entre plusieurs fils d'exécution

Refs, agents et atomes pour gérer l'état est une leçon Clojure Functional Programming & JVM Backend Development gratuite sur CoddyKit. Ceci est la leçon 1 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Clojure Functional Programming & JVM Backend Development, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Clojure Functional Programming & JVM Backend Development comprend 4 leçons au total.

Certaines parties de cette leçon n'ont pas encore été traduites et s'affichent en anglais.

Concurrency & State Intro

Managing state in concurrent programs is tricky! When multiple parts of your code try to change the same data at once, you can run into big problems like data corruption or deadlocks.

Clojure offers powerful tools to handle shared mutable state safely and efficiently.

Clojure's State Philosophy

Clojure embraces immutability by default. This means data structures don't change after creation. But what about when you genuinely need to change something, like a counter or a user's balance?

  • Clojure provides special concurrency primitives.
  • These primitives manage mutable state in a controlled way.
  • They ensure changes are safe, even across multiple threads.

Atoms: Independent State

An Atom is the simplest way to manage a single, independent piece of mutable state. It's great for things like counters or flags.

  • Updates are synchronous and atomic (all-or-nothing).
  • Atoms use a Compare-And-Swap (CAS) loop internally.
  • Best for state that doesn't need to be coordinated with other pieces of state.

Atom Code Example

Let's see an Atom in action. We'll create a simple counter and update it.

(ns coddykit.core
  (:gen-class))

(defn -main
  "Demonstrates Clojure Atoms."
  [& args]
  (let [my-counter (atom 0)]
    (println "Initial value:" @my-counter)

    (swap! my-counter inc)
    (println "After inc:" @my-counter)

    (swap! my-counter + 5)
    (println "After add 5:" @my-counter)))

Atom `swap!` Explained

The swap! function is key for updating Atoms:

  • It takes the atom, a function, and optional arguments for that function.
  • (swap! my-atom inc) atomically increments the value.
  • (swap! my-atom + 5) atomically adds 5 to the value.
  • The function is applied to the atom's current value, and the result becomes the new value. This happens in a thread-safe way.

Refs: Coordinated State

Refs are for managing coordinated mutable state. This means you have multiple pieces of state that must change together, or not at all.

  • Refs use Clojure's Software Transactional Memory (STM).
  • Changes occur within a transaction (dosync block).
  • All changes in a transaction either succeed or fail as a group.

Ref Code Example

Here's how to use Refs to transfer a value between two accounts atomically. If one fails, both revert.

(ns coddykit.core
  (:gen-class))

(defn -main
  "Demonstrates Clojure Refs and STM."
  [& args]
  (let [account-a (ref 100)
        account-b (ref 50)
        amount-to-transfer 20]

    (println "Initial: A=" @account-a "B=" @account-b)

    (dosync
      (alter account-a - amount-to-transfer)
      (alter account-b + amount-to-transfer))

    (println "After transfer: A=" @account-a "B=" @account-b)))

STM with Refs Explained

The dosync block defines a transaction:

  • ref creates a new Ref.
  • alter changes a Ref's value within a transaction. It takes the Ref, a function, and its arguments.
  • If any part of the dosync block fails, all changes are rolled back.
  • This ensures atomicity: all or nothing.

Agents: Asynchronous State

Agents are designed for managing mutable state asynchronously and in isolation. They're perfect for tasks that might take time or run in the background without blocking your main thread.

  • Updates are sent as messages to the Agent.
  • The Agent processes these messages in a separate thread.
  • This provides isolation: the agent's state changes only through its own processing.

Agent Code Example

Let's use an Agent to process a list of numbers asynchronously, summing them up.

(ns coddykit.core
  (:gen-class))

(defn -main
  "Demonstrates Clojure Agents."
  [& args]
  (let [sum-agent (agent 0)]
    (println "Initial sum:" @sum-agent)

    (send sum-agent + 10)
    (send sum-agent + 20)
    (send sum-agent + 5)

    (println "Waiting for agent to finish...")
    (await sum-agent) ; Wait for all sent actions to complete
    
    (println "Final sum:" @sum-agent)))

Agent Workflow

Here's how Agents work:

  • agent creates a new Agent with an initial value.
  • send dispatches a function and arguments to the agent. The function will be applied to the agent's current state on another thread.
  • The agent processes messages one by one in its own thread, ensuring isolated updates.
  • await (or await-for) is used to block the current thread until the agent has processed all pending actions.

Choosing the Right Primitive

You've learned about Atoms, Refs, and Agents. Each has a specific purpose for managing mutable state safely.

Which of the following statements correctly describe when to use which concurrency primitive?

Recap: State Management

In this lesson, we explored Clojure's core concurrency primitives for managing mutable state safely:

  • Atoms: For simple, independent, synchronous state changes.
  • Refs: For coordinated, transactional state changes across multiple values using STM.
  • Agents: For asynchronous, isolated state updates, processed in a separate thread.

Understanding these tools is crucial for building robust and concurrent Clojure applications!

Questions Fréquemment Posées

La leçon « Refs, agents et atomes pour gérer l'état » est-elle gratuite ?

Oui — le texte complet de « Refs, agents et atomes pour gérer l'état » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Clojure Functional Programming & JVM Backend Development, passe à CoddyKit PRO. Le cours Clojure Functional Programming & JVM Backend Development comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Refs, agents et atomes pour gérer l'état » ?

Apprenez à utiliser les primitives de concurrence fondamentales de Clojure pour gérer un état modifiable en toute sécurité entre plusieurs fils d'exécution Tu pratiques Clojure Functional Programming & JVM Backend Development avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer Clojure Functional Programming & JVM Backend Development ?

Aucune expérience préalable n'est requise. Clojure Functional Programming & JVM Backend Development sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 1 sur 4.

Combien de temps prend la leçon « Refs, agents et atomes pour gérer l'état » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon Clojure Functional Programming & JVM Backend Development ?

Oui. Chaque leçon Clojure Functional Programming & JVM Backend Development inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Refs, agents et atomes pour gérer l'état
  2. Mémoire transactionnelle logicielle (STM)
  3. Promesses, futures et opérations asynchrones
  4. Channels core.async et blocs Go
← Retour à Clojure Functional Programming & JVM Backend Development