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-threads
library for multi-threading support in Common Lisp.Instead of Go’s
atomic.Uint64
, we use anatomic-integer
from thebordeaux-threads
library.The
sync.WaitGroup
is 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 threads
to wait for all threads to complete, similar towg.Wait()
in the Go version.The final value is printed using
format
instead offmt.Println
.
To run this program:
$ sbcl --load atomic-counters.lisp
* (atomic-counters:main)
ops: 50000
We 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.