I'm a bit confused about just what thread::wait does. The manual says it should be the very last command in the script. I'm assuming that also
applies to using it in a loop.
I was thinking about some code in a thread that would accept some data
to work on, and use thread::wait to wait for something to be sent. But
it seems I can't do what I would normally think of as a standard work
loop like so:
set t1 [thread::create {
while 1 {
thread::wait
.. wake up when something arrives ...
process arrival
send back result
}
} ]
Also, on the thread::send there's the ?varname? last arg. Can it
retrieve the result more than once if more than one thread::send is
done? What if a send occurs while an earlier one is still processing?
Does it queue them up?
Since code is sent, rather than data, will they all trigger together
before it even wakes up?
Or do I have this completely wrong?
What's the best way to do this (best = simplest in this case)?
parent thread code (assume Tk is running):
set t1 [thread::create {
proc do-something {a b c} {
lappend result [do-something1 $a] ;# assume do-something
lappend result [do-something2 $b]
lappend result [do-something3 $c]
# assume this thread was 'informed' somehow of the parent thread ID
# (tsv is useful for this)
thread::send -async $parent [list the-answer-is $result]
}
}
proc the-answer-is {result} {
display-the-answer $result ;# assume 'display-the-answer' does
# whatever necessary to show/print/output/etc. the answer
}
button .b -text "Do something" \
-command [list thread::send -async $t1 do-something $z $y $x]
# assume z y x are -textvars connected to other widgets
This is sort of a RPC/message hybrid concept. The individual async
thread send's are passing messages around. The messages execute
"remote procedures".
I'm a bit confused about just what thread::wait does. The manual says it should be the very last command in the script. I'm assuming that also
applies to using it in a loop.
ted brown <tedbrown888@gmail.com> wrote:
I'm a bit confused about just what thread::wait does. The manual says it
should be the very last command in the script. I'm assuming that also
applies to using it in a loop.
No, you don’t need a loop. Call thread::wait directly and you‘re set. And if you create the thread without a script, with [thread::create], then the thread waits automatically.
But it seems that you want to do something that is not achievable with thread::wait, namely sending signals between threads and let one thread
wait for another to finish some work etc. This is best done with thread::condition variables. It’s also where the loop comes into the game. Condition variables enable you to wait in a thread for something that
happens in another thread, you can use them to signal there and back
from/to the threads in your program.
Two things though, puts is my usual debugger, and on windows I can't get
puts to go to a console (from inside a thread::create script) nor can I
see any errors pop up. It seems the thread just quits silently if I mess up.
How do you debug your threads?
On 8/30/2021 10:17 PM, EL wrote:
ted brown <tedbrown888@gmail.com> wrote:
I'm a bit confused about just what thread::wait does. The manual says it >>> should be the very last command in the script. I'm assuming that also
applies to using it in a loop.
No, you don?t need a loop. Call thread::wait directly and you?re set. And
if you create the thread without a script, with [thread::create], then the >> thread waits automatically.
But it seems that you want to do something that is not achievable with
thread::wait, namely sending signals between threads and let one thread
wait for another to finish some work etc. This is best done with
thread::condition variables. It?s also where the loop comes into the game. >> Condition variables enable you to wait in a thread for something that
happens in another thread, you can use them to signal there and back
from/to the threads in your program.
Thanks, I'll look into the condition variables once I fully understand
what Rich wrote.
Two things though, puts is my usual debugger, and on windows I can't get
puts to go to a console (from inside a thread::create script) nor can I
see any errors pop up. It seems the thread just quits silently if I mess up.
How do you debug your threads?
On 8/30/2021 8:23 PM, Rich wrote:
Hmmm, interesting. I think I see, just a few follow-up questions.
parent thread code (assume Tk is running):
set t1 [thread::create {
proc do-something {a b c} {
lappend result [do-something1 $a] ;# assume do-something
lappend result [do-something2 $b]
lappend result [do-something3 $c]
# assume this thread was 'informed' somehow of the parent thread ID >> # (tsv is useful for this)
thread::send -async $parent [list the-answer-is $result]
}
}
So, this seems similar to wish/tclsh running a script where if wish,
reaching the end of the script file it enters the event loop, while if started by tclsh, it exits instead - or one uses an explicit vwait.
So, analogously reaching the end of the thread script also either enters
the event loop (if started with wish) or exits (if started by tclsh).
Is that the right way to think about it?
And so the thread::wait does what the vwait does in the regular script
when you startup with tclsh.
Now with regular events, if the code triggered by an event does a vwait itself, then the next queued event will trigger, which in our case here,
with just the 1 proc, would re-enter do-something interrupting the
earlier call to do-something. Is that correct?
So to keep do-something from being interrupted, one must not do
anything, like an update, or vwait. Or else, I guess to keep them
sequential, one would have to use some thread locking primitives.
proc the-answer-is {result} {
display-the-answer $result ;# assume 'display-the-answer' does
# whatever necessary to show/print/output/etc. the answer
}
So, this proc is similar to one that might be called via the script arg
in a bind call except the "bounded" event is the thread::send back to
the parent.
button .b -text "Do something" \
-command [list thread::send -async $t1 do-something $z $y $x] >> # assume z y x are -textvars connected to other widgets
Is z, y, and x, being connected to widgets just as an example, or is
there something special about those 3 variables?
Also, I thought the script had to be in one arg, should that be
-command [list thread::send -async $t1 [list do-something $z $y $x]]
This is sort of a RPC/message hybrid concept. The individual async
thread send's are passing messages around. The messages execute
"remote procedures".
Thanks so much, I think I now have a way to think about it that is
not too different from what I'm used to - assuming my above thinking
is correct :)
How do you debug your threads?
By not using Windows as the OS.... :) (yes, really).
And, as the master parent thread 'can' open a console on Windows, I can
see the 'puts' messages there. Or if I've used Tcllib's log, it allows redirecting the log output to a file.
ted brown <tedbrown888@gmail.com> wrote:
Two things though, puts is my usual debugger, and on windows I can't get
puts to go to a console (from inside a thread::create script) nor can I
see any errors pop up. It seems the thread just quits silently if I mess up. >>
How do you debug your threads?
When you use tclsh on windows from a cmd or powershell rather than wish,
the puts goes on the cmd/ps. That’s how I do it…
But admittedly I don’t use the Threads package a lot, I usually work with threads in C/C++ in Tcl extensions. Just stumbled over condition variables
in the Threads package documentation recently, and found out that it’s the same concept as with C++ std::condition_variable. Not really surprising, though, but I was surprised that they also exist at the Tcl level ;)
I tried several ways to rename and/or proc replace [puts] inside a tcl
thread with no success. I have a collection of tools that use puts to
list globals, widget trees, etc. But with no console, I don't yet have
a way to run them.
What I've just now tossed together is a putz command that creates a
toplevel and a text widget. I'm pretty happy with how well Tk can run
inside a thread, which was mentioned in that posting comparing processes
to threads.
On 8/31/21 3:43 PM, ted brown wrote:
I tried several ways to rename and/or proc replace [puts] inside a tcl
thread with no success. I have a collection of tools that use puts to
list globals, widget trees, etc. But with no console, I don't yet
have a way to run them.
What I've just now tossed together is a putz command that creates a
toplevel and a text widget. I'm pretty happy with how well Tk can run
inside a thread, which was mentioned in that posting comparing
processes to threads.
I was fortunate to learn about threads from a mentor several years ago
and he had a nice trick for this that I have been using almost verbatim
since (this is from memory so hopefully no embarrassing mistakes):
In the main app:
proc got_puts_from_sub {msg} {
puts "$msg"
flush stdout
}
In the sub-thread:
proc puts_sub {msg} {
thread::send -async MAIN_INTERP_ID [list got_puts_from_sub $msg]
}
Then, in the sub-thread, whenever I need to use puts, I simply use
puts_sub. You can replace MAIN_INTER_ID with the actual parent interp's thread id in several ways.
On 8/31/2021 1:37 AM, EL wrote:
ted brown <tedbrown888@gmail.com> wrote:I'm hoping I can use condition variables to build my own layer on top of threads
On 8/31/2021 12:43 PM, ted brown wrote:
On 8/31/2021 1:37 AM, EL wrote:
ted brown <tedbrown888@gmail.com> wrote:I'm hoping I can use condition variables to build my own layer on top
of threads
I found a perfect example in Ashok's book "The Tcl programming
language". Section 22.11.2 on condition variables has as its example,
just what I need, with a condition variable, mutex, and a shared
variable list. Made to order!
On 01.09.2021 06:04, ted brown wrote:
On 8/31/2021 12:43 PM, ted brown wrote:
On 8/31/2021 1:37 AM, EL wrote:
ted brown <tedbrown888@gmail.com> wrote:I'm hoping I can use condition variables to build my own layer on top
of threads
I found a perfect example in Ashok's book "The Tcl programming
language". Section 22.11.2 on condition variables has as its example,
just what I need, with a condition variable, mutex, and a shared
variable list. Made to order!
Ashok has also created a promise module and wrote some articles about it:
https://tcl-promise.magicsplat.com/ https://www.magicsplat.com/blog/tags/promises/
If your objective is to just run some code in parallel from the main
thread and wait for the results of these computations, then you might
find this interesting as well.
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 285 |
Nodes: | 16 (2 / 14) |
Uptime: | 23:18:39 |
Calls: | 6,448 |
Files: | 12,050 |
Messages: | 5,254,172 |