Mutexes in Lisp
In this example, we’ll demonstrate how to use a mutex to safely access shared data across multiple threads in Lisp. We’ll use the Common Lisp implementation for this example.
(defpackage :mutexes
(:use :cl :bordeaux-threads))
(in-package :mutexes)
;; Container holds a hash table of counters; since we want to
;; update it concurrently from multiple threads, we
;; add a mutex to synchronize access.
(defclass container ()
((mutex :initform (make-lock))
(counters :initform (make-hash-table :test #'equal))))
;; Lock the mutex before accessing counters; unlock
;; it at the end of the function using unwind-protect
;; (which is similar to defer in other languages).
(defmethod inc ((c container) name)
(with-slots (mutex counters) c
(with-lock-held (mutex)
(setf (gethash name counters)
(1+ (gethash name counters 0))))))
(defun main ()
(let ((c (make-instance 'container)))
;; Initialize the counters
(setf (gethash "a" (slot-value c 'counters)) 0)
(setf (gethash "b" (slot-value c 'counters)) 0)
;; This function increments a named counter
;; in a loop.
(flet ((do-increment (name n)
(dotimes (i n)
(inc c name))))
;; Run several threads concurrently; note
;; that they all access the same Container,
;; and two of them access the same counter.
(let ((threads (list
(make-thread (lambda () (do-increment "a" 10000)))
(make-thread (lambda () (do-increment "a" 10000)))
(make-thread (lambda () (do-increment "b" 10000))))))
;; Wait for the threads to finish
(mapc #'join-thread threads)
;; Print the final state of the counters
(format t "~a~%" (slot-value c 'counters)))))
;; Run the main function
(main)
This Lisp code demonstrates the use of mutexes to manage concurrent access to shared state. Here’s a breakdown of the key components:
We define a
container
class that holds a hash table of counters and a mutex.The
inc
method increments a named counter, using the mutex to ensure thread-safe access.In the
main
function, we create an instance of the container and initialize its counters.We define a local function
do-increment
that increments a named counter in a loop.We create three threads that concurrently increment the counters.
We wait for all threads to finish using
mapc
andjoin-thread
.Finally, we print the state of the counters.
To run this program, you’ll need to have a Common Lisp implementation installed, along with the Bordeaux Threads library for multi-threading support. Save the code to a file (e.g., mutexes.lisp
) and load it into your Lisp REPL:
(load "mutexes.lisp")
The output should show that the counters were updated as expected:
#<HASH-TABLE :TEST EQUAL :COUNT 2 {10023FB5B3}>
You can inspect the hash table to see the final counter values:
(maphash #'(lambda (k v) (format t "~a: ~a~%" k v))
(slot-value c 'counters))
This should output:
a: 20000
b: 10000
This example demonstrates how to use mutexes in Lisp to safely manage shared state in a multi-threaded environment, similar to the original Go example.