为Clojure中的多个阅读器/单个写入器同步线程

问题描述:

我有一些非线程安全的代码(一个共享数据的编写器),它只能以串行方式从多个线程中调用,但我不想阻止任何其他线程安全的工作(多个读者),当这个代码没有被调用。为Clojure中的多个阅读器/单个写入器同步线程

这本质上是一个多读者/单写作者类型锁定的情况,作家需要排除读者和其他作者。

即我有两个功能:

(defn reader-function [] ....) // only reads from shared data 

(defn writer-function [] ....) // writes to shared data 

并与多个正在运行的(可能在一个循环中)的线程执行以下操作:

(do 
    (reader-function) 
    ... 
    (writer-function)) 

如果任何单个线程正在执行的写入器功能,所有其他线程必须阻止。即在任何一个时刻或者:

  • 一个线程正在执行 作家和所有 他人被阻塞
  • 多个线程 执行读取功能,可能的一些线程是 阻塞等待执行 作家一旦所有读者完成

在Clojure中实现这种同步的最佳方式是什么?

查看java.util.concurrent.locks.ReentrantReadWriteLock。这个类允许你有多个阅读器一次不会在一个作家中相互竞争。

+0

RRWL将做到这一点,但你不应该使用它们来解决Clojure中的这个问题。如果您使用RRWL来保护您的数据,那么您没有充分利用Clojure的不可变数据结构,引用或STM。 Clojure的Clojure数据结构参考已经允许多个读者。因为它们是不可改变和持久的,所以它们可以在没有阻止作者的情况下随时解除。阅读http://clojure.org/refs。 – 2011-01-22 15:41:42

+0

谢谢拉尔夫!看起来像是我所需要的。任何想法,如果有一个Clojure等价物或我需要使用Java互操作来获得此功能? – mikera 2011-01-23 17:28:33

将您的数据存入ref。数据应该是Clojure数据结构(不是Java类)。使用dosync来创建读写周围的事务。

例子。由于您将作者分成单独的函数,因此该函数必须修改参考文件alter。这样做需要交易(dosync)。您可以依赖于只在dosync中调用的作者,但是您也可以在写入内部放置一个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它读取共享裁判是很重要的。这不会阻止并发作者。