Clojure - 为什么在阻塞插入通道时执行会挂起?(core.async)

Mar*_*ark 9 clojure core.async

请考虑以下代码段:

(let [chs (repeatedly 10 chan)]
  (doseq [c chs]
    (>!! c "hello"))
  (doseq [c chs]
    (println (<!! c))))
Run Code Online (Sandbox Code Playgroud)

执行此操作将永远挂起.这是为什么?

如果我这样做(go (>! c "hello")),它的工作正常.

Leo*_*hin 15

要进行异步放置,请使用 clojure.core.async/put!

(let [chs (repeatedly 10 chan)]
  (doseq [c chs]
    (put! c "hello"))
  (doseq [c chs]
    (println (<!! c))))
Run Code Online (Sandbox Code Playgroud)

这在这个例子中起作用,<!!因为所有必要的放置都是异步发生的,因此总是会解锁.请注意以下事项:

  • 阻塞用作不同进程之间的同步约束
  • >!!<!!阻止主线程.go例程在主线程上运行,但是它们的代码通过宏扩展进行修改,以便执行控制被反转,并且可以根据core.async通道阻塞/缓冲逻辑的规则依次停止/执行它们.该技术通常被称为IOC(控制反转)状态机.
  • ClojureScript只有一个线程.因此它的实现core.async甚至不包含>!!/ <!!.如果您编写的代码与ClojureScript兼容,则只需从go-routines中的通道中获取或从传递给它们的高阶函数中调用它们,take!并始终执行in- goroutines或use put!.

(go (>! ch v))相当于(put! ch v)

是的,但不一样.put!是一个围绕该core.async.impl.protocols/WritePort put!方法的通道实现的API包装器.宏扩展(go (>! ch v))最终在同一方法调用中发生,但将其包含在许多生成的状态机代码中,以便可能停止放置操作并暂停执行go-routine,直到消费者准备好从ch(尝试(macroexpand `(go (>! ch v)))自己).产生一个go-block只进行一次异步推送操作是一种浪费,并且比立即调用更糟糕put!.go产生并返回一个额外的通道,你可以从中获取它的实体.这使您可以等待在您的示例中不打算执行的执行(针对异步操作).

  • `(put!c)`相当于`(go(>!c))`` (3认同)

She*_*ter 5

该通道没有缓冲区,并且>!!正在阻塞.有关此确切情况,请参阅示例.由于这个原因,它们产生了第二个线程 - 以防止阻塞主线程.使用goroutine,就像你的问题一样,按照类似的原则运作.

您还可以使用一些缓冲区空间创建通道 - (chan 1)