BBS水木清华站∶精华区
发信人: cybergene (基因~也许以后~~), 信区: Linux
标 题: Tcl Threading Model
发信站: BBS 水木清华站 (Thu Dec 14 15:59:19 2000)
Tcl Threading Model
TclPro Extensions | Wrap TclPro | Compile Tcl | Stub Libraries | Threads
| Windows Extensions | Regular Expressions | I18N
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.1 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 is still compiled without thread support and without
script-level access to threads. To use threads you need to can use the
testthread command that was added to tcltest for the Tcl 8.1 test suite.
This was turned into its own extension, Thread, 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 Thread 2.1 manual page.
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.
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.
static ExitHandler *firstExitPtr = NULL;
TCL_DECLARE_MUTEX(exitMutex)
Later, in the code that references firstExitPtr:
Tcl_MutexLock(&exitMutex);
exitPtr->nextPtr = firstExitPtr;
firstExitPtr = exitPtr;
Tcl_MutexUnlock(&exitMutex);
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,
typedef struct ThreadSpecificData {
/*
* This variable holds the list of nested ChannelHandlerEventProc
* invocations.
*/
NextChannelHandler *nestedHandlerPtr;
/*
* List of all channels currently open.
*/
Channel *firstChanPtr;
/*
* Static variables to hold channels for stdin, stdout and stderr.
*/
Tcl_Channel stdinChannel;
int stdinInitialized;
Tcl_Channel stdoutChannel;
int stdoutInitialized;
Tcl_Channel stderrChannel;
int stderrInitialized;
} ThreadSpecificData;
static Tcl_ThreadDataKey dataKey;
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):
void
TclFinalizeIOSubsystem()
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
Channel *chanPtr; /* Iterates over open channels.
*/
Channel *nextChanPtr; /* Iterates over open channels.
*/
for (chanPtr = tsdPtr->firstChanPtr; chanPtr != (Channel *) NULL;
chanPtr = nextChanPtr) {
nextChanPtr = chanPtr->nextChanPtr;
/* code omitted */
}
}
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:
Tcl_MutexLock(&myMutex);
while (condition_is_false) {
Tcl_ConditionWait(&myConditionVariable, &myMutex, NULL /* no
timeout */);
}
Tcl_MutexUnlock(&myMutex);
and
Tcl_MutexLock(&myMutex);
/* set shared state */
Tcl_ConditionNotify(&myConditionVariable);
Tcl_MutexUnlock(&myMutex);
Thread C API documentation
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.
Reader's Comments
The following commands for the thread extension v2.1 haven't been
documented yet:
thread::mutex,
thread::cond,
thread::sv_get,
thread::sv_exists,
thread::sv_set,
thread::sv_incr,
thread::sv_append,
thread::sv_lappend,
thread::sv_array,
thread::sv_unset
sv_* means shared variable
-- David Gravereaux, September 15 18:35:05, 2000
--
桃花坞里桃花庵,桃花庵下桃花仙;桃花仙人种桃树,又摘桃花卖酒钱。
酒醒只在花前坐,酒醉换来花下眠;半醒半醉日复日,花落花开年复年。
但愿老死花酒间,不愿鞠躬车马前;车尘马足富者趣,酒盏花枝贫者缘。
若将富贵比贫贱,一在平地一在天;若将贫贱比车马,他得驱驰我得闲。
别人笑我忒疯癫,我笑他人看不穿;不见五陵豪杰墓,无花无酒锄做田。
※ 来源:·BBS 水木清华站 smth.org·[FROM: 202.204.7.234]
BBS水木清华站∶精华区