Explain how Tk isn't thread-safe [is it Tk that isn't thread safe, or is it the windowing system underneath Tk?], 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 g 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 [2]
Explain Tcl (and Tk) generation options.
[3] remarks on history and use of Tcl, particularly as compared to Perl and Python. It also references John Ousterhout's Thread paper [4] [while the previous link currently does not appear to be available, http://home.pacbell.net/ouster/threads.pdf is a PDF of the invited Talk that Dr. O gave which may correspond in some way to what was previoussly being referenced.] which argues for event-based programming rather than thread programming.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 [5]) 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" [6] 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 ApproachHow 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 foreverMarty Backe 21 Feb 2002AW 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 [7]? Would you like to cooperate with the development of a TIP?elfring 2003-11-06 Are you prepared for [thread safety] [8]?
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
ttrace::eval source myfile.tclCommands 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
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 foreverThe reason is described here [9]. 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 ThreadErrorThere 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 foreverAlternatively, you can use the technique under 'A Cookbook Approach' above to send a command back to your main thread.Also see "Concurrency concepts".
AM (26 august 2008) Here is a Modest tool for simulating threads, its purpose is to provide insight in what a multithreading program (or the relevant synchronising parts of it) is doing. Mind you: a very modest tool.
[Punana] (5 may 2009) I tried using the thread callback in wish, on no matter what I did, the thread was never able to find the callback proc in the main thread. So, eventually I found a work around, Instead of using "thread::errorproc ThreadError" for an error reporting method, I manually generate an error inside the thread, and use the errorproc callback to do what I wanted in the first place.
