Prepare multi-threading with core.async
This increasingly popular pattern used when creating
multi-threaded applications. The idea is not new, its design is founded in 1978.
as CSP.The most famous implementation of the now universally used in Golang.
We're in the paper, we consider the implementation of the CSP core.for async in Clojure, if you're interested, welcome to under the cat.
the article discusses the simple and basic practices for working with core.async described in the article would be enough for a good start in multi-threaded programming.
In contrast to Golang where the paradigm of threading through the channels built into the language core.async is just a library for Clojure, if you are impressed with another paradigm, then the choice is: pulsar, promesa, manifold
this core.async and promesa can also be used on the browser side in ClojureScript, of course, in this case, none of what multithreading can not speak, because all that stuff is compiled into ES5 and executed in a browser, but a familiar and comfortable interface to work with the async may well serve.
So what gives us the core.async? If to explain on fingers that core.we provides async dispatch through go-blocks in your fixed Thread Pool with 8 threads (the Thread Pool size can be changed using a special option). When a message arrives in the channel core.async itself will find the free flow, and will give him a task, or put the message in the queue. Who first hears about the Thread Pool, you can read a note on pattern Worker Thread
Example 1
the
(defonce log-chan (chan))
(defn loop-worker [msg]
(println msg))
(go-loop []
(let [msg (<! log-chan)]
(loop-worker msg)
(recur)))
In the example above, we create a channel log-chan
, and determined the function of a loop-worker that will process messages from the channel.Then created a go-block with the endless cycle, put our loop-worker
.Now we can send data to a channel: (>!! log-chan "Hello")
the Function loop-worker was submitted separately for the go-block intentionally, for the sake of convenient debugging via the REPL.
the body of a go-loop as this macro is baked somewhere inside the core.async, and recompiling it on the fly in the REPL is a strange character, so the handler is easier to make separately and live in peace.
it is worth noting that there is no infinite loop in the usual understanding of go-loop does not.
After receiving the message, is a one-time execution of the handler function, and then go block is parked with <!
which will wait for a new message. Thus, you can create a lot of channels and handlers for them.
within the go-block read function from the channel <!
carries out the Parking of the thread.
Outside the go block it is possible to use to read from the channel function <!!
which blocks the main thread until the message is received. The behavior of <!!
can be compared with the function of the await keyword in ES7.
Parking is a go block, it is a term core.async means that the thread is released and available for other tasks. There is also the term blocking, which means that the stream will be immediately blocked and is not available for new task until it is freed.
In example # 1 there is a flaw, if the loop-worker will be called Exception
, it will interrupt the execution of the form, and (recur)
will never be called, thus waiting for data from the channel log-chan
will stop, correct this in example # 2.
Example 2
the
(defonce log-chan (chan))
(defn loop-worker [msg]
(throw (Exception. "my exception message")))
(go-loop []
(let [msg (<! log-chan)
res (try
(loop-worker msg)
:ok
(catch Exception e
(println (.getMessage e))
:error))]
(recur)))
In this example, we wrapped the whole call loop-worker in the form try
and res
will contain a flag indicating a successful execution or error. This flag can be useful, for example, if we want to close the channel in case of an error. A working example of this approach can be viewed tut
the
(let [R1 (go (<! (timeout (rand-int 1000))) 5)
c2 (go (<! (timeout (rand-int 1000))) 7)]
(go (let [v1 (<! c1)
v2 (<! c2)]
(println {:v1 v1
:v2 v2
:summ (+ v1 v2)}))))
This example will wait for the result of all asynchronous operations listed in the let
. This practice is very convenient to solve the callback hell in JavaScript, and another reason to be glad that it can be used on the browser side in the face of ClojureScript.
Example 4
the
(defn upload
"upload emulator"
[headshot c time]
(go (Thread/sleep time)
(>! headshot c)))
(let [c1 (chan) c2 (chan)]
(upload "pic1.jpg" c1 30)
(upload "pic2.jpg" c2 40)
(let [[headshot channel] (alts!! [c1 c2 (timeout 20)])]
(if headshot
(println "Sending headshot notification for" headshot)
(println "Timed out!"))))
In this example we have created a upload function emulates an asynchronous operation, in this case a file download. The last argument upload, accepts the delay time in milliseconds. Using the function alts!!! we can get the first result that we will return one of the following in the vector channels. In our vector, the last channel is (timeout 20)
, this channel will return the result through 20 milliseconds, and this will be the first value to be written to the variable headshot
and continue running the form. Thus, this example emulates the time setting for timeout, during which we wait to perform a set of asynchronous operations.
Example 5
the
(def ping (chan))
(def pong (chan))
(go-loop []
(let [msg (<! ping)]
(when (= msg :ping)
(println msg)
(>! pong :pong)
(Thread/sleep 1000))
(recur)))
(go-loop []
(let [msg (<! pong)]
(when (= msg :pong)
(println msg)
(>! ping :ping)
(Thread/sleep 1000))
(recur)))
(>!! ping :ping)
an Example of communication of two channels, classic Ping-Pong.
It was the last example that I wanted to show. Separately it is also worth to highlight the presence of clojure's data types, created specifically for the recording of information in multiple threads, it is atom and agent as well as the General immutablelist other types, it's all very makes life easier for the developer when developing a multithreaded application.
Useful links:
" http://clojure.com/blog/2013/06/28/clojure-core-async-channels.html
"https://github.com/clojure/core.async
"https://github.com/clojure/core.async/wiki/Getting-Started
"http://www.braveclojure.com/core-async/
"http://go.cognitect.com/core_async_webinar_recording
Комментарии
Отправить комментарий