Title here
Summary here
In the previous example we saw how to manage simple counter state using atomic operations. For more complex state we can use a mutex to safely access data across multiple threads.
#lang racket
(require racket/hash)
; Container holds a hash of counters; since we want to
; update it concurrently from multiple threads, we
; add a semaphore to synchronize access.
(struct container (semaphore counters))
; Lock the semaphore before accessing `counters`; unlock
; it at the end of the function using dynamic-wind.
(define (inc c name)
(dynamic-wind
(lambda () (semaphore-wait (container-semaphore c)))
(lambda ()
(hash-update! (container-counters c) name add1 0))
(lambda () (semaphore-post (container-semaphore c)))))
(define (main)
(define c (container (make-semaphore 1)
(hash 'a 0 'b 0)))
; This function increments a named counter
; in a loop.
(define (do-increment name n)
(for ([i (in-range n)])
(inc c name)))
; Run several threads concurrently; note
; that they all access the same `container`,
; and two of them access the same counter.
(define threads
(list (thread (lambda () (do-increment 'a 10000)))
(thread (lambda () (do-increment 'a 10000)))
(thread (lambda () (do-increment 'b 10000)))))
; Wait for the threads to finish
(for-each thread-wait threads)
(println (container-counters c)))
(main)
Running the program shows that the counters updated as expected.
$ racket mutexes.rkt
'#hash((a . 20000) (b . 10000))
In this Racket version:
semaphore
instead of a mutex, as Racket doesn’t have a direct equivalent to Go’s sync.Mutex
.container
is implemented as a struct containing a semaphore and a hash table (Racket’s equivalent of Go’s map).inc
function uses dynamic-wind
to ensure the semaphore is properly released, similar to Go’s defer
.thread
function to create concurrent threads, which are similar in concept to goroutines.WaitGroup
, we simply collect the threads in a list and use for-each
with thread-wait
to wait for all threads to complete.This example demonstrates how to use semaphores in Racket to safely access shared state from multiple threads, achieving the same goal as the original Go program with mutexes.
Next we’ll look at implementing this same state management task using only threads and channels.