我的程序如下工作:两个期货在一些随机等待时间之后将一个特定金额从一个余额A转移到余额B.每次重复做10次。所以我尝试停止程序并解决未来,但它不起作用,它抛出NullPointerException,因为我deref它。我做错了什么?
我想要一个最终结果,在程序结束前的最后一行显示balanceA和balanceB。现在它确实输出结果,但它出现在"转移"并且"尝试"。我该怎么办?
谢谢。
(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
答案 0 :(得分:6)
有几个地方你有一些额外的()
,其中任何一个都可能导致NullPointerExceptions
(@futureA)
表示首先从ref获取当前值,然后获取该值并将其作为函数调用。
(do(
(println "Trying" futureNum waitingTime)
(println "Transfered" futureNum @agentCount)
(send-off agentCount inc))))
()
之后do
的额外集合导致它将打印结果称为“尝试”作为函数,谁的第一个参数是打印“转移”的结果,第二个参数是调用send-off的结果。 由于println始终返回nil
,因此会产生NPE 。这些()
似乎是无意的。
除了顶级表单之外,调用def
是不常见的(除非您正在编写宏)。在这种情况下,只有futureA和futureB的最后一次出现才有用。可能值得考虑使用for
代替然后保存序列中所有期货的结果,以便您可以看到其中任何是否失败,而不是仅仅能够判断是否最后一个是失败的。
答案 1 :(得分:1)
对于那段代码,还有许多事情可能被认为是有问题的。我会稍微清理一下,给你一些如何解决这类问题的参考。希望你不介意。
明确自己想做的事情:
balanceA
减少一些。balanceB
增加相同金额。显然,你有两个“活动部件” - 天平 - 这些是相互独立的。如果您阅读this blog post之类的内容,您会发现此处状态管理的正确选择是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
不受未来线程池大小的限制,但future
在这种情况下也适用。)你不需要取消引用它的未来run - 仅当你想等待它完成时。
这意味着如果你想等待你创建的所有期货,你必须将它们存储在某个地方。 (在您的情况下,使用def
不仅是单一的,而且还只保留最后创建的未来。)
(defn start-transfer!
[n id amount max-delay-ms]
(doall
(for [_ (range n)]
(future (transfer! id amount (rand-int max-delay-ms))))))
这将创建执行转移的期货清单(实际上是 seq )。您可以存储此列表,然后取消引用单个元素以等待所有事务完成:
(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
这样的行。
这里有很多,但我认为如果你花时间思考这篇文章中提到的不同内容,你至少会有一些有用的见解。