是defn线程安全吗? [英] Is defn thread-safe?

查看:134
本文介绍了是defn线程安全吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我可以实时重新定义功能而没有副作用吗? is defn thread-safe?

Can I redefine function in real-time without side effects? Is defn thread-safe?

推荐答案

线程足够安全用于开发,不能用于生产。 b
$ b

使用defn重新定义函数可以打断调用它的函数,如果它们在调用更改时正在运行。它在开发中确定,因为你可以只是重新启动后,它打破。

"thread safe enough for development, not for use in production."

using defn to redefine functions can break functions that call it if they are running while the call changes. it's ok in development because you can just restart after it breaks. It's safe enough if you can control when the function you are changing is called.

defn 是一个可以解析的宏到一些像

defn is a macro that resolves to somthing like

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

,因此它创建一个函数的实例,然后将它放入 var 。 vars是一个可变数据结构,允许每个线程的值。所以当你调用defn分配所有线程将看到的基本值。如果另一个线程然后改变了var指向一些其他函数,它会改变它的副本与out影响任何其他线程。所有旧线程仍然会看到旧副本

so it creates an instance of a function and then puts it into the root binding of a var. vars are a mutable data structure to allow for per-thread values. so when you call defn that assigns the base value that all threads will see. if another thread then changed the var to point at some other function it would change it's copy with out affecting any other threads. all the old threads would still see the old copy

当你通过调用 def 重新绑定一个var的根值时(通过defn宏),你改变了每个线程的值不设置它自己的值会看到。决定设置自己的值的线程将继续看到自己设置的值,而不必担心从它们下面改变的值。

When you re-bind the root value of a var by calling def again (through the defn macro) you change the value that every thread which has not set it's own value will see. threads that have decided to set their own values will continue to see the value they themselves set and not have to worry about the value being changed out from under them.

使用函数名的var的当前值,如线程执行调用(这很重要)所见。所以如果var的值改变,那么所有未来的调用都会看到新的值;但它们只会看到根绑定或自己的线程本地绑定的更改。所以首先正常情况下只有一个根绑定:

When a function call is made the current value of the var with the name of the function, as seen by the thread doing the calling (this is important), is used. so if the value of the var changes then all future calls will see the new value; but they will only see changes to the root binding or their own thread-local binding. so first the normal case with only a root binding:

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,而不是

two threads, still no race

then we run another thread and in that thread redefine foo to return 12 instead

user=> (.start (Thread. (fn [] (binding  [foo (fn [] 12)] (println (bar))))))
nil
user=> 12

foo的值(如条形图所示)在第一个线程运行repl)

the value of foo (as seen by bar) is still unchanged in the first thread (the one running the repl)

user=> (bar)
6
user=> 






两个线程和比赛条件



接下来,我们将在没有本地绑定的线程下更改根绑定的值,并且看到函数foo的值通过在另一个线程中运行的函数更改了一半:


two threads and a race condition

next we will change the value of the root binding out from under a thread with no local binding and see that the value of the function foo changes half way through a function running in another thread:

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来改变我们的函数,那么需要做一些事情。

If the change to foo (which is called indirectly) had changed the number of arguments this would have been a crash instead of a wrong answer (which is arguably better). At this point it is fairly clear that somthing needs to be done if we want to use vars and devn for changing our functions.

你真的可能希望函数不改变中间调用,所以你可以使用线程局部绑定来保护你的self从这里通过更改在新线程中运行的函数将foo的当前值保存到其线程局部绑定中:

You really may want functions to not change mid call so you can use a thread-local binding to protect your self from this by changing the function running in the new thread to save the current value of foo into its thread-local bindings:

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] -that-uses-foo))这可以读为将一个线程局部值分配给foo的当前值的foo,它保持一致,直到绑定形式结束,并进入任何从该绑定形式调用。

The magic is in the expression (binding [foo foo] (code-that-uses-foo)) this could be read as "assign a thread local value to foo of the current value of foo" that way it stays consistent until the end of the binding form and into anything that is called from that binding form.

vars足够好,可以保存你的函数,并重新定义它们在你开发代码的心中的内容。使用代码在部署的系统上使用vars自动重新定义函数将不太明智。不是因为vars不是线程安全的,而是因为在这种情况下vars是错误的可变结构来保存你的函数。 Clojure对于每个用例都有可变结构,并且在需要通过运行事务保持一致的函数的快速自动编辑的情况下,你最好将函数保存在refs中。 *

vars are good enough to hold your functions and redefine them to your hearts content while developing code. using code to automatically redefine functions very quickly on a deployed system using vars would be less wise. Not because vars are not thread safe, but because in this context vars are the wrong mutable structure to hold your function. Clojure has mutable structure for every use case and in the case of rapid automated editing of functions that need to stay consistent through the running of a transaction you would do better to hold you're functions in refs. What other language lets you choose the structure that holds your functions!*


  • 不是一个真正的问题,任何函数式语言都可以做到这一点

这篇关于是defn线程安全吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆