我们使用 Clojure 构建 "规则引擎"。每个函数代表一条规则,元数据描述了规则并提供了一些静态配置。该系统是不可变的且是并发的。
如果有两个或更多的线程并发调用相同的 Var,由于 AReference#meta() 是同步的,它们最终会相互阻塞(请参阅附图,红色点处)。
(defn
^{:rule {:remote-address "127.0.0.1"}}
example
[request]
(let [rule (:rule (meta #'example))]
(= (:remote-address rule) (:remote-address request))))
*方法:* 用 rwlock 替换同步块以实现更高的读并发性。这种方法消除了元数据的读写干扰(参见评论中的实际案例)。然而,它也有一些缺点:
* 每个 AReference(所有命名空间、vars、atoms、refs 和 agents)都有一个额外的字段
* 将锁的构建过程加入到了 AReference 的构建过程中(影响性能和启动时间)
* 补丁:clj-1888-2.patch 将同步替换为 rwlock 以实现更高的读并发性
* 替代方案:*
* 对于 _meta 使用 volatile,对于 alter/reset 使用 synchronized。允许在 volatile 的下一次读取 _meta,这是否足够安全?
* 从 ReentrantReadWriteLock 扩展出 AReference,而不是持有它——这很奇怪,但可能会有不同的、潜在的更好的内存/构建占用。