The following came from the not uncommon Question/Answer pair Q: I Want Threads A: You Have An [Event] Loop [AW]: But I have multiple processors/cores. mailto:uwe-klein@foni.net (Uwe Klein) wrote in [comp.lang.tcl]: Your answer says only that tcl has an event loop, but gives no explamation as to the differences / advantages. 1st Try: POLLED see if any in a list of jobs have to be done and process them. loop till dead. THREAD In a threaded program one will do each "primitive" loop of action in its own thread. thread1 { block in read ; read file ; process read ; signal whats been done } thread2 { block on user input ; process user input ; signal for crt update } thread3 { ... Where threads interact they have to be synced somehow. EVENT In [event-oriented programming] one will set up an event handler for anything that should / will happen. event1 { fileevent readable datafile { do what must be done i.e. put data into dataarray } } event2 { writing to dataarray { compute depending values } } event2a { writing to dataarray { update crt } } event3 { button EXIT { clean up and exit } event4 { ... Generated events are queued and then processed by the event loop. If all dependencies are described correctly through "trace variable" , "fileevent", "widget -command" , .. any internal/external stimulus will cascade down its described dependecies. Think of an event-looped program as something equivalent to a Makefile for your functional requirements. Caveat: if your code contains "computing monoblocs" your program becomes unresponsive. [AMG]: Try moving these long calculations into child processes. These programs take all their input from '''stdin''' or the command line arguments, write their results to '''stdout''', and abort by writing to '''stderr''' and [[[exit]]]ing nonzero. If you want progress feedback, have them (optionally?) write that to '''stdout''' as well, and update your GUI when progress data is received. Run the child process with [[[set] $chan [[[open] |[[[list] ''program'' ''arg1'' ''arg2'' ''...'']] a+]]]] and give it a [[[fileevent] $chan readable]] handler. [[[puts] $chan]] all the data it needs. Such programs can do one thing and quit, or stay running as long as your program runs. (Example: a DNS lookup program, to dodge blocking gethostbyname().) These programs can be written in any language (be open to the possibility that another language, say C, might be more appropriate, like in the DNS case), which is a nice feature, and also a good demonstration of Tcl's power as an integration (glue) language. Also these programs can be run outside of your script; maybe they're useful all by themselves, as command line tools. [slebetman] 3 Jan 06: AMG's "open" method unfortunately doesn't work on Windows for long running programs since you'll only get the program's stdout at the moment the program exits (hence no parent-child communication). This 'bug' currently affects all versions of Windows up to WinXP. [UK] 3 Jan 06: i found blt's bgexec more elegant than either exec or pipe, it is available on win. I don't know if it has the same issues ( i think not). ---- [Sarnold] 2 Jul 06: I can give a short example of such [event-oriented programming]. This was the solution to keep my [GUI] alive during long computations of the [Mancala] game, allowing the player to cancel the game. I could not do that with Threads, although I thought it would be nice. Each time the computer was 'thinking', it freezed the whole GUI. Bonus: when the user cancels a computation, the background process is killed -- so it can not waste CPU time anymore. ====== catch {package require Tclx} proc exe-refresh {chan} { global BIN GUESS if {$BIN eq $chan} {return} if {$BIN ne ""} { # needs TclX catch {kill [pid $BIN]} # no, close does not terminate the attached process, at least on Win XP catch {close $BIN} } set BIN $chan set GUESS "" } proc eventually {chan} { global BIN GUESS # if the current channel has been cancelled, # close the channel, free the resource if {$chan ne $BIN} { catch {close $chan} return } # read until the end of the process # stores the chosen pit into a global variable append GUESS [read $chan] if {[eof $chan]} {close $chan} } proc make-bin-best-move {board player nest awelemode islight} { set fd [open [concat | cmancala.exe $board $player $nest $awelemode $islight]] fileevent $fd readable [list eventually $fd] global GUESS BIN exe-refresh $fd update tkwait variable GUESS set pit $GUESS if {$BIN ne $fd} {error "operation cancelled by user"} set BIN "" set GUESS "" # restores the config if {![string is integer $pit]} {error "internal error: integer expected, got $pit"} return $pit } #... # somewhere in a Tk callback # cancels the move and stops the background process exe-refresh "" # init set BIN "" set GUESS "" ====== ---- Note that the authors of "Why Events Are A Bad Idea" [http://www.usenix.org/events/hotos03/tech/full_papers/vonbehren/vonbehren_html/index.html] explicitly claim compatibility with [JO]s' recommendations; their analysis of threading focuses on certain high-performance servers, and moreover emphasizes the well-documented ''duality'' between [concurrency concepts]. [UK] the central isssue with [event-oriented programming%|%events] is that they can/will not utilise SMP Systems. An interesting idea would be to have easily migratable [interp]'s: package require mip ;# the imaginary migratable interp package set interp [ mip::interp create ] mip::migrate -tothread $interp mip::migrate -toremote $rhost:$rport $interp ... [NEM] notes that separate ''processes'' can take advantage multiple processors/cores just as easily as multiple threads. So, e.g. farming out intensive processing to background tasks using exec or open as suggested above would help with this. I'd also note that it is the shared state aspect of threads that make them particularly tricky to program with. Something like [Erlang]'s message-passing lightweight processes are much simpler (and would probably work very nicely with a Tcl-like event model). <> Arts and crafts of Tcl-Tk programming | Concurrency | Threads