Channel Synchronization in Clojure

Our example demonstrates how to use channels for synchronization between different threads in Clojure. This is similar to using channels to synchronize execution across goroutines in other languages.

(ns channel-synchronization
  (:require [clojure.core.async :as async]))

;; This is the function we'll run in a separate thread. The
;; 'done' channel will be used to notify another thread
;; that this function's work is done.
(defn worker [done]
  (print "working...")
  (Thread/sleep 1000)
  (println "done")
  ;; Send a value to notify that we're done.
  (async/>!! done true))

(defn -main []
  ;; Start a worker thread, giving it the channel to
  ;; notify on.
  (let [done (async/chan 1)]
    (async/thread (worker done))
    ;; Block until we receive a notification from the
    ;; worker on the channel.
    (async/<!! done)))

;; Run the main function
(-main)

To run this program:

$ clj -M channel-synchronization.clj
working...done

In this Clojure version, we’re using the core.async library which provides Go-like channels and CSP-style concurrency. The async/thread function is used to start a new thread, similar to launching a goroutine.

The worker function simulates some work by sleeping for a second, then sends a value on the done channel to signal completion.

In the -main function, we create a channel, start the worker in a new thread, and then wait for the worker to finish by trying to receive from the done channel.

If you removed the (async/<!! done) line from this program, the program would exit before the worker even started, similar to the behavior in the original example.

Note that in Clojure, we typically use >!! and <!! for blocking put and take operations on channels, respectively. The double-bang (!!) indicates that these are blocking operations.