Getting Started
Requirements
Loading
(asdf:load-system :cl-threads)
The package is cl-threads. Its names are short and general (make-thread, make-lock, parallel-for), so use it through a local nickname rather than :use:
(defpackage #:example
(:use #:cl)
(:local-nicknames (#:th #:cl-threads)))
(in-package #:example)
The examples on this wiki use th:.
A first pool
A job pool is a set of worker threads waiting for work. Make one, give it work, and destroy it when you are done:
(th:with-job-pool (pool :workers 4)
(let ((squares (make-array 1000)))
(th:parallel-for pool 0 1000
(lambda (start end)
(loop for i from start below end
do (setf (aref squares i) (* i i)))))
(aref squares 999)))
;; => 998001
What happened:
with-job-poolstarted four worker threads.parallel-forsplit the integers 0 to 999 into chunks, 16 of them by default, four per worker. The workers each called the function on chunks, as(function chunk-start chunk-end), until none were left.parallel-forreturned once every chunk was done.with-job-poolstopped the workers and waited for them.
The function writes to disjoint parts of one array, so the workers never touch the same element and need no lock.
A pool for a program's lifetime
A game engine makes one pool at start-up and keeps it until it quits. Keep it with the rest of the program's state, such as the object that runs the game, and pass it to what needs it, rather than in a global variable, which everything in the image shares:
(defun run-game ()
(th:with-job-pool (pool)
;; ... the whole game runs here, and passes POOL to what needs it ...
))
Without :workers, a pool has one worker for each processor beyond two, which an engine keeps for its main and render threads. (th:default-worker-count) says how many that is. cl-threads counts the processors on Linux and Windows; elsewhere the default is one worker, so pass :workers there.
Starting work and carrying on
parallel-for waits. To start work and carry on, submit it to a job group:
(let ((group (th:submit-job (th:make-job-group pool)
(lambda () (decode-texture "grass.png")))))
;; ... the frame goes on ...
(when (th:jobs-done-p group)
(use-the-texture)))
See Job Groups.
Running the tests
stift test
The suite covers threads and joining, locks, semaphores and timeouts, parallel-for and nesting, job groups, every way errors are handled, worker storage, and that parallel-for allocates nothing in steady state.
Next: Job Pools.