October 4, 2008
Threads in SBCL
Working with threads in SBCL isn't obvious in some respects.
I'd like to use this post to collect some hints for people starting out with multi-threaded programming in SBCL.
Prerequisite: SBCL compiled with threads (still not the default). Check with
Spawns a new thread executing the lambda and return it.
The thread will silently terminate when the lambda returns. You can wait for its completion using JOIN-THREAD.
'nuff said.
(not (null (member :sb-thread *features*))).
Usage
All thread stuff lies in the SB-THREAD package, so you need to import it when you don't want to do prefixing:(use-package :sb-thread)
Creating a thread
(make-thread (lambda () ...) :name "optional name")
Listing threads
(list-all-threads)
Debugging threads
Use RELEASE-FOREGROUND to switch between multiple threads waiting for input:CL-USER(15): (make-thread (lambda () (break))) debugger invoked on a SIMPLE-CONDITION in thread #< thread RUNNING {AA9E831}>: break #< thread RUNNING {AA9E831}> CL-USER(16): (release-foreground) Resuming thread #< thread RUNNING {A93CD49}> Type HELP for debugger help, or (SB-EXT:QUIT) to exit from SBCL. restarts (invokable by number or by possibly-abbreviated name): 0: [CONTINUE ] Return from BREAK. 1: [TERMINATE-THREAD] Terminate this thread (#< thread RUNNING {A93CD49}>) (BREAK "break")3 0] (release-foreground) Resuming thread #< thread "initial thread" RUNNING {A6DD551}> CL-USER(17):
Cleaning up
Perhaps this is obvious to someone familiar with the stack semantics of threading:CL-USER(25): (make-thread (lambda () (unwind-protect (break) (format t "cleanup~%")))) #<thread RUNNING {AB89DB9}> CL-USER(26): debugger invoked on a SIMPLE-CONDITION in thread #< thread RUNNING {AB89DB9}>: break (release-foreground) Resuming thread #< thread RUNNING {AB89DB9}> Type HELP for debugger help, or (SB-EXT:QUIT) to exit from SBCL. restarts (invokable by number or by possibly-abbreviated name): 0: [CONTINUE ] Return from BREAK. 1: [TERMINATE-THREAD] Terminate this thread (#< thread RUNNING {AB89DB9}>) (BREAK "break") 0] 1 Resuming thread #< thread "initial thread" RUNNING {A6DD551}> 1 CL-USER(27): cleanup </thread>
Comments(1)
When speaking about threads, locks and synchronization objects and some other aspects should also be mentioned.
Sbcl provides mutexes, semaphores and condition variables. In principle, it is enough to implement other synchronization objects. But it lacks message queues, worker thread pool, etc.
Also, it is worth to mention special variables. In SBCL, all threads share values of special variables, but have independent bindings and do not inherit bindings. So, dynamic variables act as thread-local variables. This has some consequences, for example, with standard IO. For example, Slime rebinds *standard-output* to another value, and newly created threads print to another stream.