Szukaj…
podstawowe operacje na kanałach: tworzenie, umieszczanie, pobieranie, zamykanie i bufory.
core.async
polega na tworzeniu procesów, które pobierają wartości i umieszczają je w kanałach .
(require [clojure.core.async :as a])
Tworzenie kanałów za pomocą chan
Kanał tworzysz za pomocą funkcji chan
:
(def chan-0 (a/chan)) ;; unbuffered channel: acts as a rendez-vous point.
(def chan-1 (a/chan 3)) ;; channel with a buffer of size 3.
(def chan-2 (a/chan (a/dropping-buffer 3)) ;; channel with a *dropping* buffer of size 3
(def chan-3 (a/chan (a/sliding-buffer 3)) ;; channel with a *sliding* buffer of size 3
Wprowadzanie wartości do kanałów za pomocą >!!
i >!
Wstawiasz wartości do kanału za pomocą >!!
:
(a/>!! my-channel :an-item)
Możesz umieścić dowolną wartość (ciągi, liczby, mapy, kolekcje, obiekty, a nawet inne kanały itp.) W kanale, z wyjątkiem nil
:
;; WON'T WORK
(a/>!! my-channel nil)
=> IllegalArgumentException Can't put nil on channel
W zależności od bufora kanału, >!!
może zablokować bieżący wątek.
(let [ch (a/chan)] ;; unbuffered channel
(a/>!! ch :item)
;; the above call blocks, until another process
;; takes the item from the channel.
)
(let [ch (a/chan 3)] ;; channel with 3-size buffer
(a/>!! ch :item-1) ;; => true
(a/>!! ch :item-2) ;; => true
(a/>!! ch :item-3) ;; => true
(a/>!! ch :item-4)
;; now the buffer is full; blocks until :item-1 is taken from ch.
)
Z bloku (go ...)
możesz - i powinieneś - użyć a/>!
zamiast a/>!!
:
(a/go (a/>! ch :item))
Logiczne zachowanie będzie takie samo jak a/>!!
, ale zamiast rzeczywistego wątku systemu operacyjnego zostanie zablokowany tylko logiczny proces goroutine.
Używanie a/>!!
wewnątrz bloku (go ...)
znajduje się anty-wzór:
;; NEVER DO THIS
(a/go
(a/>!! ch :item))
Pobieranie wartości z kanałów za pomocą <!!
Bierzesz wartość z kanału za pomocą <!!
:
;; creating a channel
(def ch (a/chan 3))
;; putting some items in it
(do
(a/>!! ch :item-1)
(a/>!! ch :item-2)
(a/>!! ch :item-3))
;; taking a value
(a/<!! ch) ;; => :item-1
(a/<!! ch) ;; => :item-2
Jeśli żaden element nie jest dostępny na kanale, a/<!!
zablokuje bieżący wątek, dopóki wartość nie zostanie wstawiona do kanału (lub kanał zostanie zamknięty, patrz później):
(def ch (a/chan))
(a/<!! ch) ;; blocks until another process puts something into ch or closes it
Z bloku (go ...)
możesz - i powinieneś - użyć a/<!
zamiast a/<!!
:
(a/go (let [x (a/<! ch)] ...))
Logiczne zachowanie będzie takie samo jak a/<!!
, ale zamiast rzeczywistego wątku systemu operacyjnego zostanie zablokowany tylko logiczny proces goroutine.
Używanie a/<!!
wewnątrz bloku (go ...)
znajduje się anty-wzór:
;; NEVER DO THIS
(a/go
(a/<!! ch))
Zamykanie kanałów
Zamykasz kanał za a/close!
:
(a/close! ch)
Gdy kanał zostanie zamknięty, a wszystkie dane w kanale zostaną wyczerpane, ujęcia zawsze zwracają nil
:
(def ch (a/chan 5))
;; putting 2 values in the channel, then closing it
(a/>!! ch :item-1)
(a/>!! ch :item-2)
(a/close! ch)
;; taking from ch will return the items that were put in it, then nil
(a/<!! ch) ;; => :item-1
(a/<!! ch) ;; => :item-2
(a/<!! ch) ;; => nil
(a/<!! ch) ;; => nil
(a/<!! ch) ;; => nil
;; once the channel is closed, >!! will have no effect on the channel:
(a/>!! ch :item-3)
=> false ;; false means the put did not succeed
(a/<!! ch) ;; => nil
Pakiety asynchroniczne z put!
Jako alternatywa dla a/>!!
(co może blokować), możesz zadzwonić a/put!
aby wstawić wartość do kanału w innym wątku, z opcjonalnym wywołaniem zwrotnym.
(a/put! ch :item)
(a/put! ch :item (fn once-put [closed?] ...)) ;; callback function, will receive
W ClojureScript, ponieważ blokowanie bieżącego wątku nie jest możliwe, a/>!!
nie jest obsługiwany i put!
to jedyny sposób na umieszczenie danych w kanale spoza bloku (go)
.
Asynchroniczne zabiera ze take!
Jako alternatywa dla a/<!!
(co może blokować bieżący wątek), możesz użyć a/take!
asynchronicznie pobierać wartość z kanału, przekazując ją do wywołania zwrotnego.
(a/take! ch (fn [x] (do something with x)))
Używanie buforów upuszczających i przesuwnych
Przy upuszczaniu i przesuwaniu buforów, put nigdy nie blokuje, jednak gdy bufor jest pełny, tracisz dane. Bufor upuszczający traci ostatnie dodane dane, natomiast bufory przesuwne tracą pierwsze dodane dane.
Przykład upuszczania bufora:
(def ch (a/chan (a/dropping-buffer 2)))
;; putting more items than buffer size
(a/>!! ch :item-1)
=> true ;; put succeeded
(a/>!! ch :item-2)
=> true
(a/>!! ch :item-3)
=> false ;; put failed
;; no we take from the channel
(a/<!! ch)
=> :item-1
(a/<!! ch)
=> :item-2
(a/<!! ch)
;; blocks! :item-3 is lost
Przykład bufora przesuwnego:
(def ch (a/chan (a/sliding-buffer 2)))
;; putting more items than buffer size
(a/>!! ch :item-1)
=> true
(a/>!! ch :item-2)
=> true
(a/>!! ch :item-3)
=> true
;; no when we take from the channel:
(a/<!! ch)
=> :item-2
(a/<!! ch)
=> :item-3
;; :item-1 was lost