Concurreny in Tcl: Weaving Threads


In the previous article we talked about pseudo-concurrency through events and mentioned threads as one possible solution to some of their limitations. This second article expands on that.

Threads compared to Events

First, in contrast to events, only the C level foundations are built into the core. Script access to threads requires a package: Thread.

The model espoused by both foundation and package is the so-called "apartment" model. Tcl treats threads like separate processes, which just happen to be run in the same memory space. They can talk to each other through what is essentially an explicit message-passing API.

The nice thing about this is that all the unpleasantness of race conditions and such for access to shared memory are contained in the implementation of the message-passing commands. To the scripts running in the various threads there is no shared memory. That gets rid of a whole class of potential problems outright.

Example: Thread-based Network Server

Using the network server of the previous article as example, this is how it would implemented with threads, mostly:

  1. package require Thread
  3. socket -server NewConnection 7777
  5. proc NewConnection {channel peerhost peerport} {
  6.     fconfigure $channel -buffering none
  8.     set t [thread::create {
  9.         proc HandleConnection {channel} {
  10.             while {![eof $channel]} {
  11.                 if {[gets $channel line] < 0} {
  12.                     continue
  13.                 }
  14.                 puts $channel $line
  15.             }
  16.             close $channel
  17.             thread::exit
  18.         }
  19.         thread::wait
  20.     }]
  22.     after 0 [list HandOver $t $channel]
  23.     return
  24. }
  26. proc HandOver {thread channel} {
  27.     thread::transfer $thread $channel
  28.     thread::send -async $thread [list HandleConnection $channel]
  29.     return
  30. }
  32. vwait _dummy_variable_

While there is still a bit of event management in waiting for new connections, their handling is shifted into threads. One thread is created per connection, with the necessary handler procedure written to use blocking IO calls in a regular fashion.

The interesting points in the script are the 'thread::create' command to make and initialize a new thread, the 'thread::transfer' used to shift the new socket from the main thread into the worker thread, and the 'thread::send' to run scripts in other threads, with 'thread::wait' waiting for incoming requests made by 'thread::send'.

Note that this code is simplicistic, killing the thread of a connection whenever the user is done with it. In a proper application there would very likely be some sort of thread-pool keeping released threads around for re-use by new connections, to reduce the overhead associated with thread creation and destruction.


Our examples so far have had lots of concurrency, but with no need for communication between the parts at all. Now that we know about the principle of bringing concurrency to Tcl processes, only one issue is left to discuss: How can we get the multiple parts of a concurrent application to talk to each other? We'll cover that in the next post.