gpt4 book ai didi

clojure - defn 线程安全吗?

转载 作者:行者123 更新时间:2023-12-02 06:52:58 26 4
gpt4 key购买 nike

我可以在没有副作用的情况下实时重新定义功能吗? defn 线程安全吗?

最佳答案

“线程对开发来说足够安全,而不是在生产中使用。”

使用 defn 重新定义函数可能会破坏调用它的函数,如果它们在调用更改时正在运行。开发中没问题,因为您可以在它中断后重新启动。如果您可以控制何时调用您正在更改的函数,那就足够安全了。
defn是一个宏,可以解析为

(def name (fn [args] (code-here)))

所以它创建一个函数的实例,然后将它放入 var 的根绑定(bind)中. vars 是一个可变的数据结构,允许每个线程的值。因此,当您调用 defn 时,它将分配所有线程将看到的基本值。如果另一个线程随后将 var 更改为指向其他函数,它将更改它的副本而不影响任何其他线程。所有旧线程仍会看到旧副本

当您通过调用 def 重新绑定(bind) var 的根值时再次(通过 defn 宏)您更改每个尚未设置其自己值的线程将看到的值。决定设置自己的值的线程将继续看到他们自己设置的值,而不必担心从它们下面更改出来的值。

单线程无种族

当进行函数调用时,将使用具有函数名称的 var 的当前值,正如执行调用的线程所看到的那样(这很重要)。因此,如果 var 的值发生变化,那么所有 future 的调用都会看到新值;但他们只会看到对根绑定(bind)或他们自己的线程本地绑定(bind)的更改。所以首先是只有根绑定(bind)的正常情况:
user=> (defn foo [] 4)
#'user/foo
user=> (defn bar [] (foo))
#'user/bar
user=> (bar)
4
user=> (defn foo [] 6)
#'user/foo
user=> (bar)
6

两个线程,仍然没有种族

然后我们运行另一个线程并在该线程中重新定义 foo 以返回 12
user=> (.start (Thread. (fn [] (binding  [foo (fn [] 12)] (println (bar))))))
nil
user=> 12

foo 的值(如 bar 所示)在第一个线程(运行 repl 的线程)中仍然保持不变
user=> (bar)
6
user=>

两个线程和一个竞争条件

接下来,我们将从没有本地绑定(bind)的线程下更改根绑定(bind)的值,并看到函数 foo 的值在另一个线程中运行的函数中途改变:
user=> (.start (Thread. (fn [] (println (bar)) 
(Thread/sleep 20000)
(println (bar)))))
nil
user=> 6 ;foo at the start of the function

user=> (defn foo [] 7) ;in the middle of the 20 seond sleep we redefine foo
#'user/foo
user=> 7 ; the redefined foo is used at the end of the function

如果对 foo 的更改(间接调用)改变了参数的数量,这将是一个崩溃而不是一个错误的答案(可以说更好)。在这一点上,如果我们想使用 vars 和 devn 来改变我们的函数,那么很明显需要做一些事情。

如何使用没有竞争条件的变量

您确实可能希望函数不更改中间调用,因此您可以使用线程本地绑定(bind)来保护您自己免受这种情况的影响,方法是更改​​在新线程中运行的函数以将 foo 的当前值保存到其线程本地绑定(bind)中:
user=> (.start (Thread. (fn [] (binding [foo foo] (println (bar)) 
(Thread/sleep 20000)
(println (bar))))))
nil
user=> 7

user=> (defn foo [] 9)
#'user/foo
user=> 7

神奇之处在于表达式 (binding [foo foo] (code-that-uses-foo))这可以理解为“将线程本地值分配给 foo 的当前值的 foo”,这样它会保持一致,直到绑定(bind)表单结束并进入从该绑定(bind)表单调用的任何内容。

Clojure 给你选择,但你必须选择

vars 足以容纳您的函数并在开发代码时根据您的内心重新定义它们。使用代码在使用 vars 的已部署系统上非常快速地自动重新定义函数将不太明智。不是因为 vars 不是线程安全的,而是因为在这种情况下 vars 是错误的可变结构来保存你的函数。 Clojure 对每个用例都有可变结构,在快速自动编辑需要通过事务运行保持一致的函数的情况下,最好将函数保存在 refs 中。还有什么其他语言可以让您选择包含您的功能的结构!*
  • 不是一个真正的问题,几乎任何函数式语言都可以做到这一点
  • 关于clojure - defn 线程安全吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5181367/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com