当我在Clojure中从多个线程中定义一个变量时会发生什么?

use*_*135 2 multithreading clojure

当我做的事情:

(def x 123)
(future (def x 456))
Run Code Online (Sandbox Code Playgroud)

所述def在所述第二线程结束修改在主线程的值.我知道这不是惯用的,我应该使用原子或更复杂的东西.然而,除此之外,这与我的期望相反,因为我在各个地方读过变量是"动态的"或"线程本地的".

那么,到底发生了什么?第二个线程是否进行了不安全的分配,类似于在C中执行等效操作时会发生什么?如果是这样,clojure是否"允许"其他不安全操作的可能性,例如从多个线程追加到列表并最终导致数据结构不一致?

Tay*_*ood 5

首先,def是Clojure中的一种特殊形式,值得一读.

我在不同的地方读过变量是"动态的"或"线程局部的".

他们可以,但这不是典型的用法.从指南:

def 始终适用于根绑定,即使var在调用def的点处是线程绑定的.

为了证明这一点:

(def ^:dynamic foo 1)
(binding [foo 2] ;; thread-local binding for foo
  (prn foo)      ;; "2"
  (def foo 3)    ;; re-defs global foo var
  (prn foo))     ;; "2" (still thread-local binding value)
(prn foo)        ;; "3" (now refers to replaced global var)
Run Code Online (Sandbox Code Playgroud)

并有多个线程:

(def ^:dynamic foo 1)
(future
  (Thread/sleep 500)
  (prn "from other thread" foo))
(binding [foo 2]
  (prn "bound, pre-def" foo)
  (def foo 3)
  (Thread/sleep 1000)
  (prn "bound, post-def" foo))
(prn "finally" foo)
;; "bound, pre-def" 2
;; "from other thread" 3
;; "bound, post-def" 2
;; "finally" 3
Run Code Online (Sandbox Code Playgroud)

那么,到底发生了什么?第二个线程是否进行了不安全的分配,类似于在C中执行等效操作时会发生什么?

取决于你对不安全的定义,但就多线程而言,它肯定是不协调的和非原子的.您可以使用alter-var-root原子更改var,或使用类似于refatom可变状态的内容.

如果是这样,clojure是否"允许"其他不安全操作的可能性,例如从多个线程追加到列表并最终导致数据结构不一致?

不是它的持久性数据结构,它在概念上是写时复制的(尽管这些副本在效率上有共同的知识).在Clojure和其他函数语言中编写多线程代码时,这会带来很多好处.当您附加到(持久性)列表数据结构时,您不会就地修改结构; 你随着你的改变得到了一个新的结构"副本".你如何处理这个新值,大概是把它放在像var,ref,atom等一些全局"桶"中,决定了变化的"安全性"或原子性.

但是,您可以轻松地从多个线程修改Java的一个线程不安全的数据结构,并最终处于不利的位置.