请分享您的看法:请填写2024 年 Clojure 状态调查!

欢迎!请查看关于页面以了解更多有关这个网站如何运作的信息。

+1
Clojure

我不确定这是否是一个错误还是预期的行为。我一直在调查一个奇怪的情况,Clojure 的绑定传递方式并没有按照我们预期的进行。下面的回放案例显示了使用 clojure.core 函数发生的问题。实际上,发生的事情是,我们使用了 clojure.lang.Agent/soloExecutor 作为 ExecutorService 用于 一个 Langohr RabbitMQ 订阅。处理 RabbitMQ 消息的线程能够读取设置在无关代码中的动态变量,这些代码是通过代理运行的。

症状

我们是这样设置动态变量的

(defn http-handler [url]
  (binding [*url* url]
    (log/info "doing a http request")))

(defn rabbit-handler [message]
  (log/info "handling a RabbitMQ message"))

我们的日志系统会抓取当前 *url* 的值并将其添加到日志行中。当我们看到日志行中来自其他无关线程对 *url* 进行记录时,这个变量应该没有值。在上面的玩具示例中,我们看到了在 "处理 RabbitMQ 消息" 的日志行中设置 *url* 的值。

观察到的行为的原因是 send-via 中的函数和 binding-conveyor-fn 设置线程绑定的方式中binding-conveyor-fn 设置 thread-local 存储中的 threadBindingFrame,并且从不在设置后解除设置。

如果相同线程随后用于其他目的,上一次使用线程时设置的动态绑定将保持不变。

复现案例

(ns binding
  (:import java.util.concurrent.Executors))

(def thread-pool
  (Executors/newFixedThreadPool 1))

(set-agent-send-off-executor! thread-pool)

(def counter (agent 0))

(add-tap println)

(def ^:dynamic *some-context* nil)

(tap> "start")

(defn inspect []
  (tap>
   (format "tap: thread=%s context=%s"
           (.getId (Thread/currentThread))
           *some-context*)))

(binding [*some-context* "in the agent"]
  (send-off counter (fn [& _]
                      (inspect))))


(.submit thread-pool inspect)

;; Output
;; start
;; tap: thread=43 context=in the agent
;; tap: thread=43 context=in the agent

代码片段

1 答案

0

编辑了

我可以补充说, neither wrapping inspect with bound-fn,就像

(def inspect (bound-fn []
  (tap>
   (format "tap: thread=%s context=%s"
           (.getId (Thread/currentThread))
           *some-context*))))

nor wrapping the inspect function at the invocation at the end of the sample

(.submit thread-pool (bound-fn* inspect))

不能修复问题。

把最后一行替换成

(.submit thread-pool (#'clojure.core/binding-conveyor-fn inspect))

却能解决了。

旧问题,相关: https://clojure.atlassian.net/browse/CLJ-2476

如果用户有足够的勇气重用 Clojure 的 soloExecutor,则存在相同的问题。可以将其设置为私有吗?

https://gist.github.com/karolinepauls/9c976d95950141bc3b7d8b179b65b1c4
...