Select in Racket

Our example demonstrates how to wait on multiple channel operations. Combining threads and channels with a select-like mechanism is a powerful feature in concurrent programming.

#lang racket

(require racket/async-channel)

(define (main)
  ; For our example we'll select across two channels.
  (define c1 (make-async-channel))
  (define c2 (make-async-channel))

  ; Each channel will receive a value after some amount
  ; of time, to simulate e.g. blocking RPC operations
  ; executing in concurrent threads.
  (thread
   (lambda ()
     (sleep 1)
     (async-channel-put c1 "one")))
  
  (thread
   (lambda ()
     (sleep 2)
     (async-channel-put c2 "two")))

  ; We'll use a loop with synchronization to await both of these values
  ; simultaneously, printing each one as it arrives.
  (for ([i (in-range 2)])
    (match (sync c1 c2)
      ["one" (printf "received ~a\n" "one")]
      ["two" (printf "received ~a\n" "two")])))

(main)

To run the program, save it as select.rkt and use the racket command:

$ time racket select.rkt
received one
received two

real    0m2.253s
user    0m0.031s
sys     0m0.015s

We receive the values “one” and then “two” as expected.

Note that the total execution time is only ~2 seconds since both the 1 and 2 second sleeps execute concurrently.

In this Racket version:

  1. We use async-channels instead of Go’s channels.
  2. Instead of goroutines, we use Racket’s thread function to create new threads.
  3. The select statement is replaced with a sync function that can wait on multiple channels.
  4. We use a for loop with match to handle the different channel cases, similar to Go’s select.

This example demonstrates how Racket can handle concurrent operations and channel-like communication between threads, similar to Go’s select mechanism.