Atomic Counters in OCaml
Here’s the translation of the atomic counters example from Go to OCaml:
Our primary mechanism for managing state in OCaml is through mutable references and modules like Atomic
. In this example, we’ll look at using the Atomic
module for atomic counters accessed by multiple threads.
open Lwt
open Lwt.Infix
(* We'll use an atomic integer to represent our (always-positive) counter. *)
let ops = Atomic.make 0
(* A function to increment the counter 1000 times *)
let increment_counter () =
for _ = 1 to 1000 do
Atomic.incr ops
done;
Lwt.return_unit
let main () =
(* We'll start 50 threads that each increment the counter exactly 1000 times. *)
let threads = List.init 50 (fun _ -> increment_counter ()) in
(* Wait until all the threads are done. *)
Lwt.join threads >>= fun () ->
(* Here no threads are writing to 'ops', but it's safe to atomically read a value. *)
Printf.printf "ops: %d\n" (Atomic.get ops);
Lwt.return_unit
let () = Lwt_main.run (main ())
To run this program, you’ll need to have the lwt
library installed. You can compile and run it using the following commands:
$ ocamlfind ocamlopt -o atomic_counters -linkpkg -package lwt.unix atomic_counters.ml
$ ./atomic_counters
ops: 50000
We expect to get exactly 50,000 operations. The use of Atomic
ensures that the increments are performed atomically, preventing race conditions that could occur if we used a regular mutable reference.
In this OCaml version:
- We use the
Atomic
module to create an atomic counter. - Instead of goroutines, we use Lwt threads for concurrency.
- The
increment_counter
function increments the counter 1000 times. - We create 50 threads using
List.init
andincrement_counter
. Lwt.join
is used to wait for all threads to complete.- Finally, we print the value of the counter using
Atomic.get
.
This approach provides similar guarantees to the original Go version, ensuring that our counter operations are atomic and thread-safe.
Next, we’ll look at mutexes, another tool for managing state in concurrent programs.