1
votes

In one of Timmy Jose's blog posts at https://z0ltan.wordpress.com/2016/09/02/basic-concurrency-and-parallelism-in-common-lisp-part-3-concurrency-using-bordeaux-and-sbcl-threads/ he gives an example of the wrong way to print to the top level from inside a thread (using Bordeaux Threads as an example, although I am using Lparallel):

(defun print-message-top-level-wrong ()
  (bt:make-thread
    (lambda ()
      (format *standard-output* "Hello from thread!")))
  nil)

(print-message-top-level-wrong) -> NIL

The explanation is that "The same code would have run fine if we had not run it in a separate thread. What happens is that each thread has its own stack where the variables are rebound. In this case, even for *standard-output*, which being a global variable, we would assume should be available to all threads, is rebound inside each thread!"

And this is exactly what happens if the function is run in Allegro CL. However, in SBCL the function does print the intended output at the terminal. Does this mean *standard-output* is not being rebound in SBCL? In general, is there a cross-platform way to print to *standard-output* from inside a thread?

In a multithreaded situation printing to the terminal should normally be coordinated to avoid potentially printing from several streams at the same time. But there don't seem to be any functions like atomic-format or atomic-print available. Is there a straightforward way to avoid printing interference when there are multiple threads (assuming that locks/mutexes are too expensive to use for each individual printing operation)?

1

1 Answers

2
votes

If you actually have a global binding (a binding in the global environment), it does work for all threads; see the documentation for bt:make-thread. Only dynamic (re-)bindings are thread-local. Implementations differ in how/when they bind those streams; sometimes the binding that is actually in effect for user programs is global, sometimes not.

I like to use some sort of queue or channel to coordinate output where necessary; I have not yet run into situations where the locking overhead was prohibitive.

Maybe you could try something with optimistic locking, but I don't know what has been done for that librarywise (some Lisp implementations do have CAS operations that could be used). This should be orthogonal to the parallelism library used.

EDIT: Just found in the SBCL manual: sb-concurrency has lock-free queues and mailboxes.