Channel Synchronization in Nim

We can use channels to synchronize execution across threads. Here’s an example of using a blocking receive to wait for a thread to finish. When waiting for multiple threads to finish, you may prefer to use a CountdownLatch or ThreadPool.

import std/[threadpool, os]

# This is the procedure we'll run in a thread. The
# `done` channel will be used to notify another
# thread that this procedure's work is done.
proc worker(done: Channel[bool]) =
  stdout.write("working...")
  stdout.flushFile()
  sleep(1000) # Sleep for 1 second
  echo "done"

  # Send a value to notify that we're done.
  done.send(true)

# Start a worker thread, giving it the channel to
# notify on.
let done = newChannel[bool]()
spawn worker(done)

# Block until we receive a notification from the
# worker on the channel.
discard done.recv()

To run the program:

$ nim c -r channel_synchronization.nim
working...done

If you removed the discard done.recv() line from this program, the program would exit before the worker even started.

In Nim, we use spawn to create a new thread, which is similar to goroutines in concept. The Channel type is used for communication between threads, analogous to channels in Go. The newChannel[bool]() creates a new channel that can send and receive boolean values.

The worker procedure simulates work by sleeping for a second, and then sends a value on the channel to signal completion. The main thread waits for this signal by calling done.recv(), which blocks until a value is received.

Note that Nim’s concurrency model is based on OS threads, which are more heavyweight than Go’s goroutines, but the synchronization concept remains similar.