简体   繁体   English

core.async 通道 - 跟踪发生的情况(示例)

[英]core.async channels - tracing what happens when (example)

I'm at Ch.我在Ch。 6 of Paul Butcher's 7 Concurrency Models in 7 Weeks , which is focussing on core.async . Paul Butcher 7 周内7 个并发模型中的 6 个,重点是core.async

We have the following function我们有以下功能

(defn map-chan [f from]                                                         
  (let [to (chan)]
    (go-loop []
      (when-let [x (<! from)]    
        (>! to (f x))            
        (println "parking channel write.") 
        (recur))                         
      (close! to))
    (println "map-chan done.")
    to)) 

I added the printlns myself, to explore exact order of computation, which I want to ask about here.我自己添加了printlns ,以探索计算的确切顺序,我想在这里询问。

We can run it like this我们可以这样运行

(def ch (to-chan (range 10)))             ; [1]
(def mapped (map-chan (partial * 2) ch))  ; [2]
(<!! (async/into [] mapped))              ; [3]

;; [1] Create & rtn a channel from els of seq, closing it when seq fin.
;; [2] map-chan returns immediately, with blocked go blocks inside of it.
;; [3] calling async/into finally triggers the parked channel writes, as seen below.

in the repl:在回复中:

channels.core=> (def ch (to-chan (range 10)))
#'channels.core/ch
channels.core=> (def mapped (map-chan (partial * 2) ch))
map-chan done.
#'channels.core/mapped
channels.core=> (<!! (async/into [] mapped))
parking channel write.
parking channel write.
parking channel write.
parking channel write.
parking channel write.
parking channel write.
parking channel write.
parking channel write.
parking channel write.
parking channel write.
[0 2 4 6 8 10 12 14 16 18]
channels.core=> 

Question

We have a (sync) (ie unbuffered) channel here that has both writer and reader to it ready to go.我们这里有一个(同步)(即无缓冲)通道,它准备好了写入器和读取器。 Why is my "parking channel write" above not triggered until async/into is called?为什么在调用async/into之前不会触发上面的“停放通道写入”? (It's not the channel read with <!! that triggers it, it's async/into itself - easy to check). (触发它的不是使用<!!读取的通道,它是async/into自身 - 易于检查)。 I'm not complaining about this, just seeking to understand why the trace is the way it is.我不是在抱怨这个,只是想了解为什么痕迹是这样的。 Are channels actually somehow lazy as well?频道实际上是否也很懒惰? He hasn't mentioned this in the book yet.他还没有在书中提到这一点。

Note the dependency on this code is org.clojure/core.async "0.1.267.0-0d7780-alpha" , if that makes any difference.请注意,对这段代码的依赖是org.clojure/core.async "0.1.267.0-0d7780-alpha" ,如果这有什么不同的话。

Also, in the book he has used a buffered channel of length 10. Yet, I also tried it with an unbuffered (sync) channel and the result seems the same.此外,在书中,他使用了长度为 10 的缓冲通道。然而,我也尝试使用无缓冲(同步)通道,结果似乎相同。

Your output channel to has a size of zero, and so the write cannot take place until a corresponding take is requested.您的输出通道to的大小为零,因此在请求相应的镜头之前无法进行写入。 Look at a modified version of your code:查看代码的修改版本:

(ns tst.demo.core
  (:use tupelo.core tupelo.test )
  (:require
    [clojure.core.async :as async]
    ))

(defn map-chan [f from]
  (let [to (async/chan)]
    (async/go
      (loop []
        (when-let [x (async/<! from)]
          (println "put - pre")
          (async/>! to (f x))
          (println "put - post")
          (recur)))
      (async/close! to))
    (println "map-chan returns output buffer")
    to))

(dotest
(println :1)
(spyx
  (def ch (async/to-chan (range 10)))) ; [1]

(Thread/sleep 2000) (println :2)
(spyx
  (def mapped (map-chan (partial * 2) ch))) ; [2]

(Thread/sleep 2000) (println :3)
(spyx
  (async/<!! (async/into [] mapped))) ; [3]
  )

with results:结果:

-------------------------------
   Clojure 1.10.1    Java 13
-------------------------------

lein test tst.demo.core
:1
(def ch (async/to-chan (range 10))) => #'tst.demo.core/ch
:2
map-chan returns output buffer
(def mapped (map-chan (partial * 2) ch)) => #'tst.demo.core/mapped
put - pre
:3
put - post
put - pre
put - post
put - pre
put - post
put - pre
put - post
put - pre
put - post
put - pre
put - post
put - pre
put - post
put - pre
put - post
put - pre
put - post
put - pre
put - post
(async/<!! (async/into [] mapped)) => [0 2 4 6 8 10 12 14 16 18]

So, the go loop does start running immediately, but the first put operation blocks until the async/into at step [3] occurs.因此,go 循环确实立即开始运行,但第一个 put 操作会阻塞,直到步骤 [3] 中的async/into发生。

If we use a buffered output channel of length 20, we see the go loop running before step [3] occurs:如果我们使用长度为 20 的缓冲输出通道,我们会看到在步骤 [3] 发生之前运行的 go 循环:

...
(let [to (async/chan 20)]
   ...

with result:结果:

:1
(def ch (async/to-chan (range 10))) => #'tst.demo.core/ch
:2
map-chan returns output buffer
(def mapped (map-chan (partial * 2) ch)) => #'tst.demo.core/mapped
put - pre
put - post
put - pre
put - post
put - pre
put - post
put - pre
put - post
put - pre
put - post
put - pre
put - post
put - pre
put - post
put - pre
put - post
put - pre
put - post
put - pre
put - post
:3
(async/<!! (async/into [] mapped)) => [0 2 4 6 8 10 12 14 16 18]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM