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水木清华站∶精华区