Clojure的承诺和期货

为并发设计的 Clojure非常适合我们的Back to the Future系列 。 此外,Clojure支持开箱即用的期货。 最后但并非最不重要的一点是,Clojure是第一个在期货promise之间进行明确区分的语言/库。 它们是如此相似,以至于大多数平台仅支持期货或将它们组合在一起。 Clojure在这里非常明确,这很好。 让我们从诺言开始:

承诺

Promise是一个封装不可变值的线程安全对象。 此值可能尚不可用,以后可以从任何线程准确地传递一次。 如果其他线程尝试在交付之前取消对诺言的引用 ,它将无限阻塞。 如果承诺
已解决(已交付),不会发生阻塞。 承诺只能交付一次,并且一旦设置就永远不能更改其价值:

(def answer (promise))
 
@answer
 
(deliver answer 42)

answerpromise 。 尝试使用@answer(deref answer)对此进行解除引用将会很简单。 该线程或其他线程必须首先为该承诺提供一些价值(使用deliver函数)。 在deref上阻塞的所有线程deref将唤醒,随后取消引用此诺言的尝试将立即返回42 。 Promise是线程安全的,以后不能修改。 试图传递另一个值来answer被忽略了。

期货

从用户的角度来看,期货在Clojure中的行为几乎相同-它们是单个值的容器(当然,它可以是maplist -但应该是不可变的),并尝试在解析无限块之前取消引用。 就像承诺一样,期货只能被解决一次,而取消引用解决的未来具有立竿见影的效果。 两者之间的区别在于语义,而不是技术。 Future表示后台计算,通常在线程池中,而promise只是一个简单的容器,任何人都可以在任何时间点进行交付(填充)。 通常,没有关联的后台处理或计算。 这更像是我们正在等待的事件(例如,我们正在等待的JMS消息回复 )。 话虽如此,让我们开始一些异步处理。 与Akka相似 ,底层线程池是隐式的,我们只需传递要在后台运行的代码。 例如,要计算低于一千万的正整数之和,我们可以说:

(let
    [sum (future (apply + (range 1e7)))]
    (println "Started...")
    (println "Done: " @sum)
)

sum是将来的实例。 在后台线程中开始计算时,将立即显示"Started..."消息。 但@sum阻止,我们实际上不得不等待一点点1看到"Done: "的消息和计算结果。 这里是其中最令人失望到达:没有future ,也没有promise Clojure中不支持收听完成/失败异步。 该API等效于非常有限的java.util.concurrent.Future<T> 。 我们可以创建futurecancel ,检查它是否realized? (已解决)并阻止其等待值。 就像Java中的Future<T>一样,事实上future函数的结果甚至实现了java.util.concurrent.Future<T> 。 尽管我非常喜欢STM和代理之类的Clojure并发原语,但期货仍然有些欠发达。 缺少事件驱动的异步回调,它们在期货完成时会被调用(请注意, add-watch无法使用期货,并且仍处于alpha状态)大大降低了期货对象的有用性。 我们不能再:

很遗憾,由于这不是技术难题,而只是缺少的API,我希望很快能看到对完成侦听器的支持。 为了完整起见,这里有一个更大的程序,它使用期货同时获取多个网站的内容,这是我们的网络爬网示例的基础:

(let [
    top-sites `("www.google.com" "www.youtube.com" "www.yahoo.com" "www.msn.com")
    futures-list (doall (
            map #(
                future (slurp (str "http://" %))
            )
            top-sites
    ))
    contents (map deref futures-list)
    ]
(doseq [s contents] (println s))
)

上面的代码开始同时下载多个网站的内容。 map deref等待所有结果,并且一旦来自futures-list所有doseq全部完成, doseq打印内容( contents是字符串列表)。

我最初陷入的陷阱是缺少doall (这会强制执行延迟序列评估)。 map会在top-sites列表之外产生延迟序列,这意味着只有在首次访问futures-list给定项时才调用future函数。 那很好。 但是每个项目仅在(map deref futures-list)期间首次访问。 这意味着在等待第一个将来被取消引用的同时,第二个未来甚至还没有开始! 它从第一个将来完成时开始,我们尝试取消引用第二个。 这意味着当所有先前的期货都已经完成时,最后的期货开始。 为了削减长话短说,不doall是强制所有期货立即开始,我们的代码按顺序运行,一个又一个的未来。 副作用之美。

1 – Scala中的BTW (1L to 9999999L).sum快了几乎一个数量级, 只是说 ...

参考: NoBlogDefFound博客中来自我们JCG合作伙伴 Tomasz Nurkiewicz的Clojure承诺和期货

翻译自: https://www.javacodegeeks.com/2013/03/promises-and-futures-in-clojure.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值