Atomic Counters in Lisp
(defpackage :atomic-counters
(:use :cl :bordeaux-threads)
(:export :main))
(in-package :atomic-counters)
(defun main ()
;; We'll use an atomic integer to represent our (always-positive) counter.
(let ((ops (make-atomic-integer :value 0)))
;; We'll start 50 threads that each increment the counter exactly 1000 times.
(let ((threads (loop repeat 50
collect (make-thread
(lambda ()
(dotimes (i 1000)
;; To atomically increment the counter we use atomic-incf
(atomic-incf (atomic-integer-value ops))))))))
;; Wait until all the threads are done.
(mapc #'join-thread threads)
;; Here no threads are writing to 'ops', but it's safe to atomically read
;; a value even while other threads might be updating it.
(format t "ops: ~A~%" (atomic-integer-value ops)))))This Lisp code demonstrates the use of atomic operations for managing a shared counter across multiple threads. Here’s a breakdown of the key elements:
We use the
bordeaux-threadslibrary for multi-threading support in Common Lisp.Instead of Go’s
atomic.Uint64, we use anatomic-integerfrom thebordeaux-threadslibrary.The
sync.WaitGroupis replaced with a list of threads that we join at the end.Go’s goroutines are replaced with Lisp threads created using
make-thread.The atomic increment operation
ops.Add(1)is replaced with(atomic-incf (atomic-integer-value ops)).We use
mapc #'join-thread threadsto wait for all threads to complete, similar towg.Wait()in the Go version.The final value is printed using
formatinstead offmt.Println.
To run this program:
$ sbcl --load atomic-counters.lisp
* (atomic-counters:main)
ops: 50000We expect to get exactly 50,000 operations. The use of atomic operations ensures that the counter is incremented correctly even with concurrent access from multiple threads.
This example demonstrates how to use atomic operations in Lisp to safely manage state across multiple threads, similar to using the sync/atomic package in Go.