3Seven Concurrency Models in Seven Weeks

函数式并发

Clojure并发

不可变性原子代理引用

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第3章:函数式并发(Functional Concurrency)

一、导读

1.1 本章学习目标

第3章介绍函数式编程在并发中的应用。函数式编程的核心——不可变性——天然适合并发。如果数据不可变,就不需要锁。

本章的核心学习目标包括:

  • 理解不可变性与并发的关系
  • 掌握 Clojure 的原子(Atom)、代理(Agent)、引用(Ref)
  • 理解函数式并发如何避免数据竞争

1.2 为什么本章如此重要

函数式并发模型证明了:通过限制可变状态,可以大幅简化并发编程。不可变数据结构、纯函数、值语义——这些函数式编程的核心概念,在并发场景中展现出巨大的优势。

1.3 核心问题引导

为什么不可变性对并发如此重要?

Clojure 的 Atom、Agent、Ref 分别适用于什么场景?

函数式并发模型能替代线程与锁吗?

持久化数据结构如何支持高效的"修改"?


二、核心概念详解

2.1 不可变性与并发

clojure
;; 不可变数据:无需锁
(def data {:name "Alice" :age 30})

;; "修改"返回新结构,原结构不变
(def updated (assoc data :age 31))

;; 多线程安全:因为数据不可变
(future (println (:name data)))   ;; 安全
(future (println (:age data)))    ;; 安全

2.2 Atom(原子操作)

clojure
;; Atom:同步的、独立的状态变更
(def counter (atom 0))

;; swap! 使用函数原子性地更新
(swap! counter inc)
(swap! counter + 5)
(deref counter)    ;; => 6
@counter             ;; => 6

;; compare-and-set
(compare-and-set! counter 6 10)

2.3 Agent(异步操作)

clojure
;; Agent:异步的、独立的状态变更
(def logger (agent []))

;; send 异步发送操作
(send logger conj "Log entry 1")
(send logger conj "Log entry 2")

;; 等待完成
(await logger)
@logger    ;; => ["Log entry 1" "Log entry 2"]

2.4 Ref(事务性操作)

clojure
;; Ref:同步的、协调的状态变更
(def account-a (ref 1000))
(def account-b (ref 500))

;; dosync 保证事务性
(dosync
  (alter account-a - 200)
  (alter account-b + 200))

;; 如果事务中的任何一步失败,整个事务回滚

三、实战案例

3.1 案例一:银行账户系统

clojure
(def accounts (ref {}))

(defn create-account [id balance]
  (dosync
    (alter accounts assoc id (ref balance))))

(defn transfer [from-id to-id amount]
  (dosync
    (let [from (get @accounts from-id)
          to (get @accounts to-id)]
      (when (and from to (>= @from amount))
        (alter from - amount)
        (alter to + amount)
        true))))

(create-account :alice 1000)
(create-account :bob 500)

;; 并发转账
(future (transfer :alice :bob 100))
(future (transfer :bob :alice 50))

四、本章小结

不可变性:消除了数据竞争的根本原因。

Atom:同步、独立的原子操作。

Agent:异步、独立的操作。

Ref:同步、协调的事务操作。


五、思考与练习

不可变数据结构的内存开销有多大?持久化数据结构如何解决?

STM 和数据库事务有什么相似之处?

函数式并发模型在什么场景下比线程与锁更合适?


3/7函数式并
0%