Sion C
Sion C

Reputation: 451

tcl multithreading like in c, having hard time to execute thread with procedure

I used to work in C, where threads are easy to create with a specific function I choose.

Now in tcl I can't use thread to start with a specific function I want, I tried this:

package require Thread

proc printme {aa} {
    puts "$aa"
}

set abc "dasdasdas"

set pool [tpool::create -maxworkers 4 ]
# The list of *scripts* to evaluate
set tasks {
    {puts "ThisisOK"}
    {puts $abc}
    {printme "1234"}
}

# Post the work items (scripts to run)
foreach task $tasks {
    lappend jobs [tpool::post $pool $task]
}

# Wait for all the jobs to finish
for {set running $jobs} {[llength $running]} {} {
    tpool::wait $pool $running running 
}

# Get the results; you might want a different way to print the results...
foreach task $tasks job $jobs {
    set jobResult [tpool::get $pool $job]
    puts "TASK: $task"
    puts "RESULT: $jobResult"
}

I always get: Execution error 206: invalid command name "printme"invalid command name "printme" while executing "printme "1234"" invoked from within "tpool::get $pool $job"

Why?

Upvotes: 0

Views: 676

Answers (1)

schlenk
schlenk

Reputation: 7247

You problem is, that the Tcl threading model is very different from the one used in C. Tcl's model is a basically 'shared nothing by default' model mostly based on message passing.

So every thread in the pool is an isolated interpreter and does not know anything about a proc printme. You need to initialize those interpreters with the procs you need.

See the docs for the ::tpool::create command, it has an option to provide a -initcmd where you can define or package require the stuff you need.

So try this to initialize your threads:

set pool [tpool::create -maxworkers -initcmd {
    proc printme {aa} {
        puts "$aa"
    }}]

https://www.tcl.tk/man/tcl/ThreadCmd/tpool.htm#M10

To answer your comment a bit more detailed:

No, there is no way to make Tcl threads work like C threads and share objects and procs freely. It is a fundamental design decision and allows Tcl to have an interpreter without a massive global lock (in contrast to e.g. CPython), as most things are thread local and use thread local storage.

But there are some ways to make initialization and use of multiple thread interpreters easier. One is the -initcmd parameter from ::tpool::create which allows you to run initialization code for every single interpreter in your pool without doing it manually. If all your code lives in a package, you simply add a package require and your interpreter is properly initialized.

If you really want to share state between multiple threads, you can use the ::tsv subcommands. It allows you to share arrays and other things between threads in an explict way. But under the hood it involves the typical locks and mutexes you might know from C to mediate access.

There is another set of commands in the thread package that allow you to make initialization easier. This is the ttrace command, which allows you to simply trace what gets executed in one interpreter and automatically repeat it in another interpreter. It is quite smart and only shares/copies the procs you really use to the target instead of loading all things upfront.

Upvotes: 6

Related Questions