【问题标题】:clojure sendoff println functionclojure sendoff println 函数
【发布时间】:2017-03-20 19:13:09
【问题描述】:

我正在尝试在事务中send-off 函数println

(ns com.lapots.functional.clojure.transact
    (:gen-class))

(defn transfer [from to amount]
    (alter
        (.balance from) - amount)
     (alter
        (.balance from) + amount))

(defrecord Account [balance])

(defn -main [& args]
    (def account1 (Account. (ref 100)))
    (def account2 (Account. (ref 100)))
    (def trx-agent (agent 0))

    (future
        (dosync
            (send-off trx-agent println "T2 transfer")
            (Thread/sleep 5000)
            (transfer account1 account2 10)))

    (dosync
        (println "T1 transfer")
        (transfer account1 account2 10))

    (shutdown-agents)
)

如果我喜欢这个

(println "T2 transfer")
(Thread/sleep 5000)

它在事务重试时显示两次消息。所以我决定使用agents让副作用操作println只运行一次。

但是当我这样做时

(send-off trx-agent println "T2 transfer")

它根本不打印T2 transfer 消息。有什么问题?

【问题讨论】:

    标签: clojure stm


    【解决方案1】:

    您使用shutdown-agents 太早了。

    (defn transfer [from to amount]
      (println :transfer-enter amount)
      (alter
        (.balance from) - amount)
      (alter
        (.balance from) + amount)
      (println :transfer-exit  amount)
    )
    
    (defrecord Account [balance])
    
    (def account1 (Account. (ref 100)))
    (def account2 (Account. (ref 100)))
    (def trx-agent (agent 0))
    
    (defn -main [& args]
      (println :main-enter )
    
      (future
        (dosync
          (println :t2-enter)
          (send-off trx-agent println "agent: T2 transfer")
          (Thread/sleep 500)
          (transfer account1 account2 20)
          (println :t2-exit)
        ))
    
      (dosync
        (println :t1-enter)
        (send-off trx-agent println "agent: T1 transfer")
        (transfer account1 account2 10)
        (println :t1-exit))
    
      (Thread/sleep 2000)
      (shutdown-agents)
      (println :main-exit )
    )
    

    结果:

    :main-enter
    :t2-enter
    :t1-enter
    :transfer-enter 10
    :transfer-exit 10
    :t1-exit
    0 agent: T1 transfer
    :transfer-enter 20
    :t2-enter
    :transfer-enter 20
    :transfer-exit 20
    :t2-exit
    nil agent: T2 transfer
    :main-exit
    

    所以 T2 现在只等待 500 毫秒,而 T1 立即运行。我们在调用 shutdown-agents 之前等待 2000 毫秒,这将杀死所有代理线程,阻止代理运行。

    【讨论】:

    • 哦,我明白了。基本上在调用 println transfer 之前,我等待 10 秒(重试事务),但到提交时,代理已关闭
    【解决方案2】:

    我发现您的代码片段至少有三处错误:

    1. 你的transfer函数

      (defn transfer [from to amount]
        (alter
          (.balance from) - amount)   ;; <======== from - amount
        (alter
          (.balance from) + amount))  ;; <======== from + amount
      

      看起来你真的想说alter from然后alter to

    2. 来自send-off 文档:

      向代理分派潜在的阻塞操作。返回代理 立即地。随后,在一个单独的线程中, agent 将设置为以下值:(应用 action-fn state-of-agent 参数)

      所以我认为你不需要那个未来。还有一行

      (send-off trx-agent println "T2 transfer")
      

      表示您正在将代理的状态设置为

      的结果
      (println state-of-agent "T2 transfer")`
      

      这是nil,因为println 总是返回nil。我不认为 这就是你想要的。

    3. transfer 函数中存在竞争条件。由于您没有以原子方式修改 fromto,因此可能会出现竞争。

    【讨论】:

    • 但是当我在事务中调用transfer 时,无论如何它都应该是原子的,不是吗?
    • 好吧,我只想显示消息。有没有办法避免显示状态?
    • > 但是当我在事务内部调用传输时,无论如何它都应该是原子的,不是吗?对,那是正确的。但是transfer 的正确性取决于“如何”调用它,这不是一个好的属性。老实说,我并不真正了解您的代码的全部目的。你到底想打印什么?
    • 一般来说,我使用函数transfer 的唯一原因是为了稍后从其他代码中显示出来哈哈。一般来说,除了测试或演示目的之外,没有任何其他目的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-12-05
    • 2022-01-11
    • 1970-01-01
    • 2022-11-20
    • 1970-01-01
    • 2012-12-13
    • 2011-05-04
    相关资源
    最近更新 更多