2024 年 Clojure 状态调查中分享您的想法!

欢迎!请参阅 关于页面了解关于如何使用本站的一些更多信息。

+1
Clojure

我不知道这是否是一个错误还是预期行为。我一直调查一个奇怪的情况,Clojure 的绑定传送方式并没有像我们预期的那样运行。下面的复现案例显示了发生的问题,仅使用 clojure.core 函数。实际上,我们已经使用 clojure.lang.Agent/soloExecutor 作为 Langohr RabbitMQ 订阅的 ExecutorService。处理 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-viabinding-conveyor-fn 设置线程绑定的方式binding-conveyor-fn 在线程局部存储中设置 threadBindingFrame,并且 never un-sets it。

如果相同的线程随后被用于其他目的,动态绑定将保持从上一个对线程使用时的状态。

复现案例

(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

Gist

1 答案

0
by
编辑了 by

我还可以补充说,既不是用 bound-fn 包装 inspect,就像

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

也不是在示例末尾调用 inspect 函数时包装该函数

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

都能解决这个问题。

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

代替最后一行才会解决问题。

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

by
如果用户足够大胆地重复使用 Clojure 的 soloExecutor,就会出现与 futures 一样的问题。可以将其设置为私有吗?

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