如何以线程安全的方式在Clojure中使用对瞬态变量的引用?

时间:2018-11-28 12:59:51

标签: parsing clojure

我有一个解析器功能,可以从文件中解析程序并将其“编译”为Clojure函数的列表以及对瞬态映射的引用。该语言是具有可变变量(A = A + 1)的简单DSL。编译后,我希望能够使用一些新数据调用Clojure函数的列表,并为可变变量获取不同的值。因为我使用瞬态映射来存储和更改可变变量,所以代码不是线程安全的。我该如何解决?有没有没有瞬态集合的实现此功能的方法?

在下面的代码中,文件script被解析并生成prog映射。这些函数位于:list中,并由ev函数执行。我在两次运行之间更改了runtime瞬态变量。

(defn read-script [script]
  (try
    (let [runtime (transient {:vars {:var1 20 :var2 ""}})
          prog    (parse (slurp script) runtime)]
      (run! ev (:list prog)) ; Calls the functions
      (println runtime) ; Show vars
      (assoc! runtime :vars {:var1 78}) ; Initialize vars
      (run! ev (:list prog))
      (println runtime prog) ; Show new vars
      ... )))

2 个答案:

答案 0 :(得分:4)

规范的线程安全,可变,不协调的对象是原子。只需使用原子即可。

瞬态不仅不是线程安全的,还必须像实际上是不可变的那样使用 ;例如:您不能像在此那样依赖瞬态图上assoc!的副作用。

答案 1 :(得分:0)

我希望得到一个更好的答案,但这就是我最终使用的答案。我的代码使用Vars。 Vars提供了一种机制来引用可变的存储位置,该位置可以使用binding在每个线程的基础上动态地反弹(到新的存储位置)。那是代码:

(def ^:dynamic runtime ())

(defn run-prog [prog runtime-init]
  (binding [runtime (transient runtime-init)] ;bind the new runtime
    (run! ev (:list prog))  ; Calls the functions
    (persistent! runtime)))

(defn read-script [script]
  (try
    (let [prog (parse (slurp script))]
      (println (run-prog prog {:vars {:var1 20 :var2 ""}}))  ; Show vars
      (println (run-prog prog {:vars {:var1 78}}))  ; Show vars
       ... )))  

run-prog由已解析的程序(函数调用列表)和变量的初始值调用。它返回变量的最终状态,并且是线程安全的(每个线程仅访问其自己的变量映射)。

如果您不想使用临时集合,则可以使用set!将整个变量映射更改为新值。