Frank <
kra...@gmail.com> wrote:
> On Thursday, March 6, 2014 8:47:37 PM UTC-6, Rich wrote:
> Hi Rich,
> Thanks again.
> As you can tell I am new to threads. :)
> The idea of having a separate thread collecting the A and B parts and
> submitting a job to the queue once the parts are available is pretty cool.
That allows for creating a "join" point, without the typical blocking wait
of a typical "join" point.
> I looked into tpool before but got stuck solving the A and B dependency as
> tpool seems to work nice when there are no dependencies.
It does work easiest when there are no interdependencies. But that is true
of any computation. The ones without interdependencies are easier to design
and code.
> Having my main thread waiting for messages from the A&B threads to submit
> my step_3(C) did not solved the problem I was having.
If by "waiting" you mean thread::wait, then that was the problem. Don't
think in terms of "C waits for A and B". Think in terms of "A pushes
answers to C" and "B pushes answers to C" and "C combines A & B once both
arrive". I.e., think of how you'd work in an office if you were manually
doing A then passing along your answer to Fred to perform C. Fred would not
sit there looming over you waiting for you to do A (well, not if he wanted
to get anything else done. Instead, when you finished an A you'd walk your
answer over to Fred's desk and put it into his inbox on the corner. At some
point after you do that, Fred would wake up from his nap, notice the A from
you, and do something with it.
> Therefore I abandoned looking into tpool. The approach of having another
> thread to do this should work fine.
The "collector" thread is what simulates the join point.
> Do you have any pointers to examples using tpool? I did some searching and
> did not come up with something meaningful.
Unfortunately, no, I do not. And there does seem to be a dearth of examples
on the web as well.
> I want to do some testing before I start ripping apart my application to
> make the changes using tpool. :) Hopefully I can code/modify an example
> similar to what I am describing here and be able to test it.
Always sage advice.
> I believe I have to do a thread::wait on the thread monitoring the A&B
> tasks completion.
No, you do not ever (if structured properly) need to do a thread::wait.
> Once the thread receives the thread::send -async from the other threads it
> can check if they associated task is done. If both tasks are done submit
> to tpool if not done same status and wait again with thread::wait.
The -async flag is why you don't ever need to touch thread::wait. The
sender, when using -async, behaves in a non-blocking way. It sends off the
result, and then returns to doing something else. The Tcl event loop
handles the idle/active state of the receiving thread.
Your "join" thread would contain a proc like this (Note - untested code):
# this assumes "result_type" is the letter "A" or the letter "B"
proc join-results { identifier result_type result_value } {
global results_buffer
if { ! [ dict exists $results_buffer $identifier ] } {
# store the partial result in results_buffer and return to the event
# loop
dict set results_buffer $identifier $result_type $result_value
} else {
# should have a pair of results now - but first a sanity check
if { [ dict exists $results_buffer $identifier $result_type ] } {
# received two A's or two B's for a single identifier - do some
# error handling here
}
# store the newly received partial result in the dict - this is so the
# two results can be ordered properly by simply retreiving them both
# by name from the dict
dict set results_buffer $identifer $result_type $result_value
# this assumes that "compute-C" takes two parameters, A and B, in that order
set final_result [ compute-C [ dict get $results_buffer $identifier A ] \
[ dict get $results_buffer $identifier B ] ]
# send final result to the GUI thread
thread::send -async [ tsv::get thread_ids gui-thread ] [ list computation-complete $final_result ]
# clean-up the results_buffer
dict unset results_buffer $identifier
}
}
Then the A and B threads/pools would submit to the collector like so (in a non-blocking manner):
thread::send -async [ tsv::get thread_ids collector-thread ] [ list join-results $result_identifier $result_type $result_value ]