Explain what [threads] are. Explain why threads are sometimes useful. Explain why threads are sometimes critical or required. Explain threading vs. thread-safety. See http://www.tcl.tk/doc/howto/thread_model.html ''Note:'' There is very little code out there in the "real world" that is actually thread-safe. The [gnu] [C++] lib has unsafe code! Thread behaviour is treacherously platform dependent. Where you may write a program that makes use of threads and seems to be well behaved, it is possible that even on the same platform the behaviour will change over time in a long running thread. The handling of exceptions from threads is, IMHO, broken on all platforms other than [Solaris] on Sparc. The glibc folks are working overtime to remedy this by fixing thread safety issues, and it is *possible* that things will improve significantly by the end of 2002. [Windows] users are on their own. [Microsoft] could whimsically implement any old craziness and you'd never know. I am not referring to the Tcl threading model, which is (apparently) designed to mitigate the effects of non-reentrant code (I admit to not being up on this, we use our own threading model by necessity.) I am referring to OS threads and their general use in Tcl extensions. Perhaps I am completely out here on my own and everyone else is talking specifically about "Tcl threads". If so then anyone who cares to may remove these comments. [AW] This rather generalizing statement dating back to before 2002 by ?? would seem to imply that no one uses threads because it's not possible. This is not true at all. Take a look at the processes on you computer and the number of threads in each as an example. E.g. on my system I see firefox using 14 threads currently, to name just one well known app. This is almost a necessity already, and many think it will be the only way forward soon, see e.g. "The free lunch is over" by Herb Sutter [http://www.gotw.ca/publications/concurrency-ddj.htm]. ---- Explain how [Tk] isn't thread-safe, but GUIs never are, and no one cares ([AW] actually, we do, see my debugging example below), because they always have a single GUI thread, with others as helpers. Windows specifically only allows a window to receive events from the thread that created the window (can that be true?!?). One should not expect to use a tcl library compiled with thread support and Tk without thread support compiled in. What you need to use is a tcl and tk both with thread support compiled in - but then be careful to not use tk in more than one thread at a time. ---- Explain threading as programming model most like (modified) assembly line. ---- David Gravereaux is expert in Tcl threading. Also, Jean-Luc Fontaine has been thinking about this deeply for his TclPython [] work. Way back in July 1999, Alexandre Ferrieux summed it up nicely ... This is related to something I've realized only recently with the Tcl threading model: a purposeful, strong isolations of threads. At the beginning, I believed that "at most one thread by interp" was a dirty hack to hide bad reentrancy in the core. Now I have come to understand that instead, it pushes forward a new (as compared to C, as you mentioned) and also very "Tclish" style: basically, Tcl threads are nearly as isolated as *processes*, which is nice because it means all the modularity and atomic-testing we want, without the fork/exec and address space switch overhead !' For the full posting see [http://groups.google.com/groups?q=&selm=377C9ED8.6032%40cnet.francetelecom.fr] ---- Explain Tcl (and Tk) generation options. ---- [http://www.sunworld.com/unixinsideronline/swol-02-2001/swol-0216-regex.html] remarks on history and use of Tcl, particularly as compared to Perl and Python. It also references [John Ousterhout]'s Thread paper [http://www.tcl.tk/doc/white.html] which argues for event-based programming in favor of threading. [TV] ''(Nov 6 '03)'' It's understandable, since threads as opposed to processes normally would offer leightweightness, which under tcl is probably not so important, and shared (global) variables and resources, which is however also possible on a per event switching basis. Note that synchronization usually takes place anyhow based on file or graphics events, though not sctrictly necessarily. A usual application for windows' threads I guess is graphics related (e.g. dialogs) which under tk is relatively seperated. ---- Point to books, tutorials, online references, etc. on best practices for thread design and programming considerations. ---- Point to critical info on testing (and debugging!) threaded apps. ---- Extensions and threading ... * The '''Thread''' extension (at SourceForge, under the tcl project [http://sourceforge.net/projects/tcl]) lifts the threading capabilities of the Tcl C-API into the scriptlevel. ---- Remember: "You may have more than one interp per thread, but never is one interp shared _across_ threads," as David Gravereaux posted to comp.lang.tcl. ---- Until the generation procedures are cleaned up, workers in this area will need to be familiar with "-DTCL_THREADS=1". ---- History of Tcl threads: [Steve Jankowski] released "mttcl" [http://www.ensta.fr/internet/solaris/mttcl.html] in 19??. [D. Richard Hipp] ... [Jim Davidson]'s work for [NaviServer] ... ---- [Zoran Vasiljevic] makes the interesting point that "If you have many threads, load Tcl interpreter in each of them and then load a module in each interpreter, then your references to a global hash table will definitely go to the same table instance. Which means you need to protect access to this table with a mutex. If you need separate tables, i.e. you need not share hash-table data between threads, then you should put the table in thread-specific data structure (TSD), so each thread gets its own copy You would also like to register some thread-exit handler which takes care about finalizing those tables on thread exit, otherwise you'll lose memory." [JMN] What sort of things will create 'a global hash table'? I see from another page that arrays are 'implemented internally by hash tables', so does this mean I have to do something fancy just to access a global array from threads? Does this even apply to using threads at the TCL scriptlevel? If not, perhaps it'd be possible for someone knowledgeable to separate out the information on this page that pertains to the C-API to keep such terrifying statements away from those who just want to use the Thread package from script. Ok.. perhaps there are some good reasons to keep people just a little wary of threads - but I'm not sure the above belongs right next to the nice welcoming 'cookbook approach' below. ---- '''A Cookbook Approach''' ''How to use the Thread2.1 package'' #!/bin/sh #\ exec tclsh "$0" "$@" package require Thread 2.1 # # Start a thread. If you will be running many long running commands # at the same time, just create multiple threads identical to this one, and # keep track of the threadID's. # set threadID [thread::create { # # From here to the 'thread::wait' statement, define the procedure(s) # that will be called from your main program (which, btw, is thread #1) # # In this case, I've defined a simple procedure that executes the command # passed into this thread. This command could be an external program or # long running tcl command. # # The result is then sent back to the main program (thread 1) via a call # to a procedure in thread 1. In this example, it's a procedure defined # at the global level. # # The 'thread::wait' is required to keep this thread alive indefinitely. # proc runCommand { ID command } { set result [eval $command] eval [subst {thread::send -async $ID \ {::printResult [list $result]}}] } thread::wait }] # # Here is the procedure that gets called from the thread when the thread # has completed its work. # proc printResult { result } { puts $result exit } proc passTheTime { } { puts [clock format [clock seconds]] after 1000 passTheTime } # # Here we define a sample command, and pass it into the previously started # thread. # set commandString "exec du -sk /usr/local" eval [subst {thread::send -async $threadID \ {runCommand 1 [list $commandString ]}}] # # In this example, lets just pass the time until the thread is complete # passTheTime vwait forever [Marty Backe] 21 Feb 2002 [AW] The above example also provides a nice way to get results from a worker thread back to your main program: have the worker thread send them back to the main thread in the same way you send work to the worker threads. Also, note that there is an equivalent way of making a command with replaced params, instead of eval [[subst ...]] : thread::send -async $threadID \ [list runCommand 1 [list $commandString ]] Also note that the main thread is not '1' on windows, so you'll need to pass the main thread ID to the worker threads. ---- [Zoran Vasiljevic] has posted an example of a logger thread and worker threads on c.l.t. (add google groups reference here): [Sharing a common logger thread example] ---- [tclguy] "just wanted to note that I have found the intermixing of thread-enabled and disabled Tcl and extensions to actually be acceptable in most contexts where only one thread was used, or if other Tcl-level threads were created, they were only used for Tcl." Which statement, while true, requires a lawyer to determine whether it has any significance relative to a project using threads. It sounds like you're saying "if you don't use threads intentionally, the multi-threading Tcl will not bite you otherwise". Is that right? I mean, is that what you meant to say; I don't think you actually know if it's right. ---- [CMcC] Each tcl core subsystem has [Thread Specific Data] ---- [[Do we make it clear that different threads can't share an interp?]] ---- [elfring] 2003-10-19 There are two thread packages. Which one do you prefer? * http://mkextensions.sourceforge.net/mkThreads12.htm * http://tcl.cvs.sourceforge.net/*checkout*/tcl/thread/doc/html/thread.html?rev=1.10 ---- [elfring] 2003-11-05 Are you interested in the improvement to support all [synchronization] primitives in these libraries [http://sourceforge.net/tracker/?group_id=10894&atid=360894&func=detail&aid=659131]? Would you like to cooperate with the development of a [TIP]? [elfring] 2003-11-06 Are you prepared for [thread safety] [http://sourceforge.net/tracker/?func=detail&aid=674367&group_id=10894&atid=360894]? ---- See [Thread-safe Tcl Extensions] for a list of extensions believed to be safe for use in multi-threaded Tcl applications. ---- One curiousity--even annoyance--of threads prior to the 2.6 release is that, while threads can communicate variables and other resources, they do '''not''' have an easy way to share [proc] definitions. The main alternatives are to: * re-[source] in definitions from the file system; or * stuff a proc definition in a variable, send the variable, and [eval] 2.6 will provide [[ttrace::eval]], as in ttrace::eval source myfile.tcl Commands proper? For the foreseeable future, each thread must [load] its own. ---- Some consolidated rules for embedding Tcl in a threaded application: * You can '''NEVER''' use the same interpreter from more than one thread * If you only have one Tcl Interpreter: * You can use either Unthreaded or Threaded Tcl * No ''Big Global Mutex'' [Big Global Mutex] is required for Unthreaded Tcl Build (Never required for Threaded Tcl build) * If you have Multiple Tcl Interpreters: * If you are using an Unthreaded Tcl ''Big Global Mutex'' [Big Global Mutex] is required for '''ALL''' calls to functions in libtcl * If you are using a Threaded Tcl, no mutex locks are required Example of all three situations (Single Interp, Unthreaded/Threaded Tcl, Multiple Interps Unthreaded Tcl, Multiple Interps Threaded Tcl) can be found in ''PNFQ'' [PNFQ], assuming it is released. ---- [AW] : Debugging threads is non-obvious at first. This does work under tclsh, but not under wish (the puts from inside the thread is not displayed in the console): package require Thread catch {console show} set ::gThread [thread::create {thread::wait} ] puts "created thread $::gThread" proc test { } { puts "test starting" thread::send -async $::gThread {puts [clock seconds]} after 2000 test puts "test ending" } test puts "started first test" #only needed for tclsh, to keep the interpreter alive and keep the event loop running vwait forever The reason is described here [http://groups.google.com/group/comp.lang.tcl/browse_thread/thread/3aca510e4e456682/e14677c573f79354]. Replacing the thread command by: thread::send -async $::gThread { tk_messageBox -message "[clock seconds]" -title "t" -type ok } Also does not work under wish (messagebox is never shown). The reason for that becomes obvious when you implement a thread error proc: proc ThreadError { thread_id errorInfo } { puts "Error in thread $thread_id. Error: $errorInfo" } thread::errorproc ThreadError There is no tk in the thread's tcl interpreter. (This is probably due to tk not being thread safe?) So finally, this works in both tclsh and wish: set ::a 0 package require Thread catch {console show} set ::gThread [thread::create {thread::wait} ] puts "created thread $::gThread" proc test { } { puts "test starting $::a" thread::send -async $::gThread { return [clock seconds] } ::a after 2000 test puts "test ending $::a" } test puts "started first test" #only needed for tclsh, to keep the interpreter alive and keep the event loop running vwait forever Alternatively, you can use the technique under 'A Cookbook Approach' above to send a command back to your main thread. ---- Also see "[Concurrency concepts]". ---- [Category Threads]