获取原子的状态,并以原子方式重置它
Get the state of an atom, and reset it atomically
我正在编写一个 Mandelbrot Set 实现,为了加快找出哪些点进入无穷大,我决定尝试使用 ExecutorService
并行检查这些点。
基本上计划是:
- 计算我需要找到的所有点
- 将每一分都交给服务
- 让服务将其结果转储到
Atom
包装 vector
中,因为它们是生成的
- 定期让绘图代码抓取生成的结果,并清空队列
我的问题是最后一点。我怎样才能安全地从原子中获取以前的结果,和重置它?
我想到了简单的方法只是:
(def draw-queue-A (atom []))
(defn grab-and-clear-queue []
(let [results @draw-queue-A]
(reset! draw-queue-A [])
results))
但这看起来不安全。如果在取消引用和 reset!
之间添加某些内容,它将丢失。
我目前确定的残暴可恶是:
(defn grab-and-clear-queue []
(let [results (atom [])]
(swap! draw-queue-A
(fn [res] (reset! results res)
[]))
results))
但是仅仅使用一个原子来检索结果似乎很荒谬。
我怎样才能理智地检索原子的内容,和重置它而不可能丢失任何结果?
一个简单的答案是使用 Clojure ref
而不是 atom
。它允许您锁定多个函数调用的值(与 swap
不同):
(ns tst.clj.core
(:use clj.core clojure.test tupelo.test)
(:require [tupelo.core :as t] ))
(t/refer-tupelo)
(t/print-versions)
(def results (ref []))
(future
(doseq [i (range 10)]
(dosync
(Thread/sleep 20)
(alter results t/append i))))
(defn get-and-clear []
(dosync
(let [curr-results @results]
(ref-set results [])
curr-results)))
(doseq [i (range 10)]
(Thread/sleep 50)
(spyx (get-and-clear)))
结果:
-------------------------------------
Clojure 1.8.0 Java 1.8.0_111
-------------------------------------
(get-and-clear) => [0 1]
(get-and-clear) => [2 3]
(get-and-clear) => [4]
(get-and-clear) => [5 6]
(get-and-clear) => [7]
(get-and-clear) => [8 9]
(get-and-clear) => []
(get-and-clear) => []
(get-and-clear) => []
(get-and-clear) => []
其他选项包括使用来自 clojure.core.async
或 a simple Java thread-safe queue.
的队列
根据问题的格式,您还可以使用 a Python-style generator function as described here.
目前有一个 JIRA ticket 处理这个请求。同时,这会做你想做的,并且类似于补丁中的内容,虽然我只浏览了代码:
(defn reset-return-old!
[atm new-value]
(let [old-value @atm]
(if (compare-and-set! atm old-value new-value)
(do
(.notifyWatches atm old-value new-value)
old-value)
(recur atm new-value))))
我们依赖于 CAS 语义,就像 swap!
does. We effectively spin to guarantee that we were not interrupted between the read and the CAS (although I suppose this still falls prey to the ABA problem 一样,但我认为在这种情况下这并不重要)。
我在上面通知手表 -- 如果你有 none,为了你的目的,你可以删除它和 do
块以进一步简化。
我正在编写一个 Mandelbrot Set 实现,为了加快找出哪些点进入无穷大,我决定尝试使用 ExecutorService
并行检查这些点。
基本上计划是:
- 计算我需要找到的所有点
- 将每一分都交给服务
- 让服务将其结果转储到
Atom
包装vector
中,因为它们是生成的 - 定期让绘图代码抓取生成的结果,并清空队列
我的问题是最后一点。我怎样才能安全地从原子中获取以前的结果,和重置它?
我想到了简单的方法只是:
(def draw-queue-A (atom []))
(defn grab-and-clear-queue []
(let [results @draw-queue-A]
(reset! draw-queue-A [])
results))
但这看起来不安全。如果在取消引用和 reset!
之间添加某些内容,它将丢失。
我目前确定的残暴可恶是:
(defn grab-and-clear-queue []
(let [results (atom [])]
(swap! draw-queue-A
(fn [res] (reset! results res)
[]))
results))
但是仅仅使用一个原子来检索结果似乎很荒谬。
我怎样才能理智地检索原子的内容,和重置它而不可能丢失任何结果?
一个简单的答案是使用 Clojure ref
而不是 atom
。它允许您锁定多个函数调用的值(与 swap
不同):
(ns tst.clj.core
(:use clj.core clojure.test tupelo.test)
(:require [tupelo.core :as t] ))
(t/refer-tupelo)
(t/print-versions)
(def results (ref []))
(future
(doseq [i (range 10)]
(dosync
(Thread/sleep 20)
(alter results t/append i))))
(defn get-and-clear []
(dosync
(let [curr-results @results]
(ref-set results [])
curr-results)))
(doseq [i (range 10)]
(Thread/sleep 50)
(spyx (get-and-clear)))
结果:
-------------------------------------
Clojure 1.8.0 Java 1.8.0_111
-------------------------------------
(get-and-clear) => [0 1]
(get-and-clear) => [2 3]
(get-and-clear) => [4]
(get-and-clear) => [5 6]
(get-and-clear) => [7]
(get-and-clear) => [8 9]
(get-and-clear) => []
(get-and-clear) => []
(get-and-clear) => []
(get-and-clear) => []
其他选项包括使用来自 clojure.core.async
或 a simple Java thread-safe queue.
根据问题的格式,您还可以使用 a Python-style generator function as described here.
目前有一个 JIRA ticket 处理这个请求。同时,这会做你想做的,并且类似于补丁中的内容,虽然我只浏览了代码:
(defn reset-return-old!
[atm new-value]
(let [old-value @atm]
(if (compare-and-set! atm old-value new-value)
(do
(.notifyWatches atm old-value new-value)
old-value)
(recur atm new-value))))
我们依赖于 CAS 语义,就像 swap!
does. We effectively spin to guarantee that we were not interrupted between the read and the CAS (although I suppose this still falls prey to the ABA problem 一样,但我认为在这种情况下这并不重要)。
我在上面通知手表 -- 如果你有 none,为了你的目的,你可以删除它和 do
块以进一步简化。