我有一些非线程安全的代码(共享数据的编写器),只能以序列化的方式从多个线程调用,但我不想阻止任何其他线程安全的工作(多个读者)当没有调用此代码时。
这实际上是一个多读者/单一作家类型的锁定情况,作家需要排除读者和其他作者。
即。我有两个功能:
(defn reader-function [] ....) // only reads from shared data
(defn writer-function [] ....) // writes to shared data
正在运行(可能在一个循环中)的一些线程如下:
(do
(reader-function)
...
(writer-function))
如果任何单个线程正在执行writer函数,则所有其他线程都必须阻塞。即在任何时候:
在Clojure中实现这种同步的最佳方法是什么?
答案 0 :(得分:3)
将您的数据放入ref
。数据应该是Clojure数据结构(不是Java类)。使用dosync
创建围绕读写的事务。
实施例。因为您将编写器拆分为单独的函数,所以该函数必须使用alter
之类的函数修改ref。这样做需要一个事务(dosync
)。你可以依赖于只在dosync中调用writer,但是你也可以在write中放入一个dosync并依赖嵌套的事务做你想做的事情 - 这使得编写者可以安全地调用进出事务。
(defn reader [shared]
(println "I see" @shared))
(defn writer [shared item]
(dosync
(println "Writing to shared")
(alter shared conj item)))
;; combine the read and the write in a transaction
(defn combine [shared item]
(dosync
(reader shared)
(writer shared item)))
;; run a loop that adds n thread-specific items to the ref
(defn test-loop [shared n]
(doseq [i (range n)]
(combine shared (str (System/identityHashCode (Thread/currentThread)) "-" i))
(Thread/sleep 50)))
;; run t threads adding n items in parallel
(defn test-threaded [t n]
(let [shared (ref [])]
(doseq [_ (range t)]
(future (test-loop shared n)))))
使用类似(test-threaded 3 10)
的内容运行测试。
此处有更多信息:http://clojure.org/refs
你没有问过这个案子,但重要的是要注意任何人都可以随时通过derefing来阅读共享引用。这不会阻止并发写入器。
答案 1 :(得分:0)
看看java.util.concurrent.locks.ReentrantReadWriteLock。这个类允许你有多个读者一次不在一个作家上相互竞争。