如何在Clojure中取消对未来的引用?

如何在Clojure中取消对未来的引用?,clojure,Clojure,我的程序工作如下:两个期货在一段随机等待时间后,将特定金额从一个余额a转移到余额B。每个人重复做10次。因此,我试图停止程序并取消对未来的引用,但它不起作用,在我取消引用时,它抛出NullPointerException。我做错了什么 我想要一个最终结果,在程序结束前的最后一行显示balanceA和balanceB。现在它确实输出结果,但它出现在“传输”和“尝试”中。我该怎么办 多谢各位 (def balanceA (ref 1000)) (def balanceB (ref 2000)

我的程序工作如下:两个期货在一段随机等待时间后,将特定金额从一个余额a转移到余额B。每个人重复做10次。因此,我试图停止程序并取消对未来的引用,但它不起作用,在我取消引用时,它抛出NullPointerException。我做错了什么

我想要一个最终结果,在程序结束前的最后一行显示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

有几个地方有一些额外的
()
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
,然后按顺序保存所有期货的结果,这样您就可以看到其中任何一个是否失败,而不是只知道最后一个是否失败

这段代码还有很多地方可能会被认为是有问题的。我会稍微整理一下,给你一些关于如何处理类似问题的参考。希望你不介意

明确自己想要做什么:

  • 打印随机等待时间
  • 将余额a减少一定数量
  • 等待所说的时间
  • 余额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)