This page describes the model Tcl uses for multi-threading, and it gives an overview of the APIs available at the Tcl and C level. The Thread Extension exposes the threading facilities described here to the Tcl script level.
One Thread Per Interpreter
Tcl lets you have one or more Tcl interpreters
(e.g., created with
Communication Among Threads
Tcl scripts in different threads can communicate by posting scripts onto the event queue of an interpreter in a different thread. This can be synchronous, where you wait for the result, or asynchronous, where your thread does not wait for the other thread to evaluate its script.
The 2.x version of the Thread Extension provides shared variables, mutexes, and condition variables so you can enjoy all the benefits, perils, and pitfalls of threaded programming. Also, if you use this extension with the new 8.4 releases you can transfer I/O channels between threads.
The Thread Package
By default, Tcl 8.4 is compiled without thread support and without script-level access to threads. To use threads you can use the testthread command that was added to tcltest for the Tcl test suite. This was turned into its own extension, Thread extension noted above, in conjunction with the Tcl 8.3.1 release. There is also a mkThread extension created by Michael Kraus. So, now you can just build Tcl 8.3 (or 8.4) with threads enabled and load the thread extension.
The C API
If you maintain an extension, you'll need to use the C APIs to make your extension thread safe. Tcl provides mutex locks, condition variables for synchronization, and thread-local storage to help manage data structures. In addition to this documentation, see the Thread-Safing Extensions page by Jeff Hobbs.
If you are making old code thread safe, then you can focus your attention on the global data structures. You'll either need to serialize all thread access by putting a Tcl_MutexLock and Tcl_MutexUnlock call around all accesses to the variable, or you may be able to move the data structure into "thread local storage".
A great source of examples is the Tcl and Tk source code itself. The following are examples taken from the sources.
Mutex Variable Example
The tclEvent.c file maintains a global list of exit handlers.
Access to this list is serialized with a mutex lock.
Later, in the code that references firstExitPtr:
Thread Local Storage / ThreadSpecificData
In many cases it is possible to have storage that is private to a thread instead of shared among threads. Access to this is cheaper because you do not need to synchronize. For example, Tcl keeps a list of I/O channels that are opened by a particular thread. As there is no sharing among interpreters in different threads, this information can be managed by each thread independently.
If you look in the Tcl sources for
you will find several examples. These are per-file declarations of the thread-local variables used in that file. For example, in tclIO.c,
Each block of thread specific data is associated with a thread "data key", which is an identifier for this particular block of thread specific data. Each thread will use the same identifier to get its own private copy of those variables. It works like this (e.g., in TclFinalizeIOSubsystem):
Condition variables are associated with a Mutex lock. The mutex is automatically released when you wait on a condition variable, and the mutex is automatically aquired when you unblock from waiting. The typical patterns of use are:
Allocation and Cleanup Issues
The Mutex, Condition Variable, and Thread Local Storage structures
in Tcl are "self-initializing". For example, there is
These objects are also cleaned up automatically when a thread is terminated. The thread-specific data is cleaned up early, right after the per-thread exit handlers are called. If you need to clean up information associated with thread specific data, use a per-thread exit handler to do it.