如何在Clojure中取消对未来的引用?
我的程序工作如下:两个期货在一段随机等待时间后,将特定金额从一个余额a转移到余额B。每个人重复做10次。因此,我试图停止程序并取消对未来的引用,但它不起作用,在我取消引用时,它抛出NullPointerException。我做错了什么 我想要一个最终结果,在程序结束前的最后一行显示balanceA和balanceB。现在它确实输出结果,但它出现在“传输”和“尝试”中。我该怎么办 多谢各位如何在Clojure中取消对未来的引用?,clojure,Clojure,我的程序工作如下:两个期货在一段随机等待时间后,将特定金额从一个余额a转移到余额B。每个人重复做10次。因此,我试图停止程序并取消对未来的引用,但它不起作用,在我取消引用时,它抛出NullPointerException。我做错了什么 我想要一个最终结果,在程序结束前的最后一行显示balanceA和balanceB。现在它确实输出结果,但它出现在“传输”和“尝试”中。我该怎么办 多谢各位 (def balanceA (ref 1000)) (def balanceB (ref 2000)
(def balanceA (ref 1000))
(def balanceB (ref 2000))
(def agentCount (agent 1))
;;transfer function
(defn transfer [balanceA balanceB amount futureNum waitingTime]
(dosync(alter balanceA - amount))
(Thread/sleep waitingTime)
(dosync(alter balanceB + amount))
(do(
(println "Trying" futureNum waitingTime)
(println "Transfered" futureNum @agentCount)
(send-off agentCount inc))))
;;futureA
(dotimes [n 10](def futureA (future (transfer balanceA balanceB 20 1 (rand-int 100)))))
;;futureB
(dotimes [n 10](def futureB (future (transfer balanceA balanceB 15 2 (rand-int 40))(prn "result" @balanceB @balanceB))))
;;print out the result
(println "result" @balanceA @balanceB)
;;dereference futures
(@futureA)
(@futureB
以下是我的输出:
user=> (load-file "assignment10.clj")
Result: Trying: Trying: 2650 2000
2 Trying: 2 Trying: 2 3
Transfered: 2 1
user=> Trying: 1 28
Transfered: 1 2
Trying: 1 28
Transfered: 1 3
Trying:Trying: 21 2218
Transfered: 2 4
Transfered: 1 4
97
Transfered: 2 6
10
Trying: 2 26
Transfered: 2 7
Transfered: 2 Trying: 8
2Trying:Transfered: 227
2 28
Transfered:7
Transfered: 2 9
2 9
Trying: 2 33
Transfered: 2 12
Trying: 1 49
Trying: 2 39
Transfered:Transfered: 21 1313
Trying: 1 57
Transfered: 1 15
Trying: 1 71
Transfered: 1 16
Trying: 1 76
Transfered: 1 17
Trying: 1 81
Transfered: 1 18
Trying: 1 93
Transfered: 1 19
Trying: 1 98
Transfered: 1 20
有几个地方有一些额外的
()
s,其中任何一个都可能导致NullPointerException
(@futureA)
意味着首先从ref中获取当前值,然后获取该值并将其作为函数调用
(do(
(println "Trying" futureNum waitingTime)
(println "Transfered" futureNum @agentCount)
(send-off agentCount inc))))
do
之后的额外的()
集使它调用打印“Trying”的结果作为函数,谁的第一个参数是打印“Transfered”的结果,第二个参数是调用send off的结果。由于println总是返回nil
,这将导致NPE。这些()
似乎是无意的
除了顶层表单之外,调用
def
是不常见的(除非您正在编写宏)。在这种情况下,只有最后发生的futureA和futureB才有用。可能值得考虑改为使用for
,然后按顺序保存所有期货的结果,这样您就可以看到其中任何一个是否失败,而不是只知道最后一个是否失败 这段代码还有很多地方可能会被认为是有问题的。我会稍微整理一下,给你一些关于如何处理类似问题的参考。希望你不介意
明确自己想要做什么:
余额b
增加相同的金额atom
。让我们使用上述方案对事务延迟进行建模:
(def balance-a (atom 1000))
(def balance-b (atom 2000))
(defn transfer!
[id amount delay-ms]
(println id ": transferring with delay of" delay-ms "milliseconds ...") ;; 1.
(swap! balance-a - amount) ;; 2.
(Thread/sleep delay-ms) ;; 3.
(swap! balance-b + amount) ;; 4.
(println id ": transfer done.")) ;; 5.
您需要使用不同值调用此函数的线程。(我希望生成Thread
s不受未来线程池大小的限制,但future
s在这种情况下也可以工作。)您不需要取消对未来的引用以使其运行—仅当您希望等待它完成时
这意味着,如果你想等待所有你创造的未来,你必须把它们储存在某个地方。(在您的情况下,使用def
不仅是一成不变的,而且只会保留最后创建的未来。)
这将创建一个执行转移的期货列表(实际上是一个序列)。您可以存储此列表,然后取消对单个元素的引用,以等待所有事务完成:
(def futures-a (start-transfer! 10 :first 20 100))
(def futures-b (start-transfer! 10 :second 15 40))
(doseq [f futures-a] @f)
(doseq [f futures-b] @f)
现在,在这一点上,没有任何东西在运行了。然后,您可以阅读余额:
(println "balances:" @balance-a "vs." @balance-b)
请注意,由于不同的println
语句会相互严重干扰,因此控制台上的输出会非常混乱。例如,这就是为什么在原始粘贴中有像Transfered:Transfered:21 1313
这样的行
这里有很多内容,但我认为如果你花时间思考本文中提到的不同内容,你至少会有一些有用的见解。不确定在这样的doseq中定义内容意味着什么。因为我试图在完成转会后结束该计划。事实上,它不起作用。您不应该在同一事务中更新两个引用吗?如果第二个因某种原因失败,第一个就成功了。谢谢。我想在最后显示“println”结果“balanceA balanceB”。我该怎么做呢?如果你把所有的期货都保存到一个列表中(而不是只保存最后一个),你可以将deref映射到该列表上,以确保它们都完成了。然后,您将知道打印最终余额是安全的。Clojure提供的其他并发功能可能值得一看,atom和代理也适用于这样的情况,即您正在使用多个身份的不协调更新。感谢您花时间真正了解此示例:)
(println "balances:" @balance-a "vs." @balance-b)