请考虑以下代码段:
(let [chs (repeatedly 10 chan)]
(doseq [c chs]
(>!! c "hello"))
(doseq [c chs]
(println (<!! c))))
执行此操作将永远挂起。那是为什么?
如果我改为(go (>! c "hello"))
,它就可以了。
答案 0 :(得分:15)
要进行异步放置,请使用clojure.core.async/put!
(let [chs (repeatedly 10 chan)]
(doseq [c chs]
(put! c "hello"))
(doseq [c chs]
(println (<!! c))))
这适用于此示例,因为<!!
将始终解锁,因为所有必要的放置都是异步发生的。请注意以下事项:
>!!
和<!!
阻止主线程。 go
例程在主线程上运行,但是它们的代码通过宏扩展进行修改,以便执行控制被反转,并且可以通过core.async
通道阻塞/缓冲逻辑的定律依次停止/执行它们。这种技术通常被称为IOC(控制反转)状态机。core.async
的实施甚至不包含>!!
/ <!!
。如果您编写的代码与ClojureScript兼容,则只需从go
中的通道中获取 - 例程或从传递给take!
的高阶函数中调出它们的值,并始终将其置于go
中 - 例程或使用put!
。 (go (>! ch v))
是否等同于(put! ch v)
?
是的,但不一样。 put!
是围绕core.async.impl.protocols/WritePort
put!
方法的渠道实施的API包装器。 (go (>! ch v))
的宏扩展最终会在同一个方法调用中发生,但会将其包含在许多生成的状态机代码中,以便可能停止放置操作并暂停执行go
- 例程,直到消费者准备好从ch
开始(尝试自己(macroexpand `(go (>! ch v)))
)。产生一个go-block只进行一次异步推送操作是一种浪费,并且比立即调用put!
更糟糕。 go
生成并返回一个额外的通道,您可以从中获取其实体。这使您可以等待在您的示例中不打算执行的执行(针对异步操作)。
答案 1 :(得分:5)
该频道没有缓冲区,>!!
正在阻止。有关此确切情况,请参阅the examples。由于这个原因,它们产生了第二个线程 - 以防止阻塞主线程。使用goroutine,就像你的问题一样,按照类似的原则运作。
您还可以使用一些缓冲区空间创建频道 - (chan 1)