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 Tcl_CreateInterp()) in each operating
system thread. However, each interpreter is tightly bound to
its OS thread and errors will occur if you let more than one
thread call into the same interpreter (e.g., with Tcl_Eval).

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.
The TCL_DECLARE_MUTEX macro declares a mutex variable if
threading is enabled, otherwise it does nothing.

The Tcl_MutexLock and Tcl_MutexUnlock calls are also macros that expand
into nothing unless you configure with --enable-threads.

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

typedef struct ThreadSpecificData

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

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
Tcl_MutexLock and Tcl_MutexUnlock, but no Tcl_MutexInit. Similarly, the first time a thread
fetches a block of thread-specific data, it is automatically allocated
and initialized to all zeros.

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.