Синхронизация потоков для нескольких читателей / одного писателя в Clojure - PullRequest
3 голосов
/ 22 января 2011

У меня есть некоторый не поточно-безопасный код (средство записи общих данных), который можно вызывать только из нескольких потоков в последовательном порядке, но я не хочу блокировать любую другую поточно-безопасную работу (несколько читателей) когда этот код не вызывается.

По сути, это ситуация блокировки типа «несколько читателей / один писатель», когда авторам необходимо исключить как читателей, так и других писателей.

т.е. У меня есть две функции:

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

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

И количество запущенных потоков (возможно, в цикле) следующее:

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

Если какой-либо отдельный поток выполняет функцию записи, все остальные потоки должны блокироваться. то есть в любое время:

  • один поток выполняет писатель и все другие заблокированы
  • несколько тем выполнение функции чтения, возможно, некоторые потоки заблокировано ожидание выполнения писатель раз все читатели закончили

Какой лучший способ добиться такой синхронизации в Clojure?

Ответы [ 2 ]

3 голосов
/ 22 января 2011

Поместите ваши данные в ref. Данные должны быть структурой данных Clojure (не классом Java). Используйте dosync для создания транзакции вокруг чтения и записи.

Пример. Поскольку вы разделяете ваш писатель на отдельную функцию, эта функция должна изменить ссылку на что-то вроде alter. Для этого требуется транзакция (dosync). Вы можете полагаться на то, что Writer вызывается только в dosync, но вы также можете поместить dosync в запись и полагаться на то, что вложенные транзакции будут выполнять то, что вы хотите - это делает Writer безопасным для вызова как внутри, так и вне транзакции.

(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

Вы не спрашивали об этом случае, но важно отметить, что любой может прочитать общедоступную ссылку, переопределив ее в любое время. Это не блокирует одновременных писателей.

0 голосов
/ 22 января 2011

Посмотрите на java.util.concurrent.locks.ReentrantReadWriteLock.Этот класс позволяет вам иметь несколько читателей, которые не конкурируют друг с другом по одному писателю за раз.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...