Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Ruby > Why not call Thread.join?

Reply
Thread Tools

Why not call Thread.join?

 
 
thefed
Guest
Posts: n/a
 
      12-31-2007
Take this code from the Ruby Cookbook:

module Enumerable
def each_simultaneously
threads = []
each { |e| threads << Thread.new { yield e } }
return threads
end
end

It is used on an array so that you may do this:
[1,2,3].each_simultaneously do |i|
sleep 5
puts i
end

And it works!

But why don't I need to call threads.each {|t| t.join }?

And if I did, would it slow it down?

Thanks,
Ari
-------------------------------------------|
Nietzsche is my copilot



 
Reply With Quote
 
 
 
 
Skye Shaw!@#$
Guest
Posts: n/a
 
      12-31-2007
On Dec 30, 9:02*pm, thefed <(E-Mail Removed)> wrote:
> Take this code from the Ruby Cookbook:
>
> module Enumerable
> * *def each_simultaneously
> * * *threads = []
> * * *each { |e| threads << Thread.new { yield e } }
> * * *return threads
> * *end
> end
>
> It is used on an array so that you may do this:
> [1,2,3].each_simultaneously do |i|
> * * * * sleep 5
> * * * * puts i
> end
>
> And it works!



What did you expect to happen?
The example you provided will do nothing but create threads and
exit.

> But why don't I need to call threads.each {|t| t.join }?


Any running threads are killed when the program exits.


> And if I did, would it slow it down?


Generally speaking, the only thing it would slow down (stop really) is
the execution path of the main thread.

Now if for some reason your main thread has to do other work, a join
would delay that, of course.
 
Reply With Quote
 
 
 
 
Robert Klemme
Guest
Posts: n/a
 
      12-31-2007
On 31.12.2007 06:45, Skye Shaw!@#$ wrote:
> On Dec 30, 9:02 pm, thefed <(E-Mail Removed)> wrote:
>> Take this code from the Ruby Cookbook:
>>
>> module Enumerable
>> def each_simultaneously
>> threads = []
>> each { |e| threads << Thread.new { yield e } }
>> return threads
>> end
>> end
>>
>> It is used on an array so that you may do this:
>> [1,2,3].each_simultaneously do |i|
>> sleep 5
>> puts i
>> end
>>
>> And it works!

>
>
> What did you expect to happen?
> The example you provided will do nothing but create threads and
> exit.
>
>> But why don't I need to call threads.each {|t| t.join }?

>
> Any running threads are killed when the program exits.
>
>
>> And if I did, would it slow it down?

>
> Generally speaking, the only thing it would slow down (stop really) is
> the execution path of the main thread.
>
> Now if for some reason your main thread has to do other work, a join
> would delay that, of course.


Nevertheless it's good practice to join. If main has other work to do
then you should join once that is done, i.e. at the end of the script.
If those threads have terminated already you basically only have the
overhead of the Threads Array iteration - but you get robustness in
return, i.e. you ensure that all those Threads can terminate properly
(assuming that they are written in a way to do that eventually).

Kind regards

robert
 
Reply With Quote
 
thefed
Guest
Posts: n/a
 
      12-31-2007

On Dec 31, 2007, at 12:49 AM, Skye Shaw!@#$ wrote:

> Generally speaking, the only thing it would slow down (stop really) is
> the execution path of the main thread.
>
> Now if for some reason your main thread has to do other work, a join
> would delay that, of course.


OK, I understand it better. But why does each {|t| t.join} join them
all at the same time (ish), and not wait for the first one to finish
executing before joining the others?


 
Reply With Quote
 
Robert Klemme
Guest
Posts: n/a
 
      12-31-2007
On 31.12.2007 17:02, thefed wrote:
> On Dec 31, 2007, at 12:49 AM, Skye Shaw!@#$ wrote:
>
>> Generally speaking, the only thing it would slow down (stop really) is
>> the execution path of the main thread.
>>
>> Now if for some reason your main thread has to do other work, a join
>> would delay that, of course.

>
> OK, I understand it better. But why does each {|t| t.join} join them
> all at the same time (ish), and not wait for the first one to finish
> executing before joining the others?


They are not joined at the same time but one after the other.

Cheers

robert
 
Reply With Quote
 
Ken Bloom
Guest
Posts: n/a
 
      12-31-2007
On Mon, 31 Dec 2007 00:02:10 -0500, thefed wrote:

> Take this code from the Ruby Cookbook:
>
> module Enumerable
> def each_simultaneously
> threads = []
> each { |e| threads << Thread.new { yield e } } return threads
> end
> end
>
> It is used on an array so that you may do this:
> [1,2,3].each_simultaneously do |i|
> sleep 5
> puts i
> end


When I ran this (not in IRB) it didn't work. The interpreter terminated
before any of the threads finished sleeping for 5 seconds. In any case,
you want to join each thread so that the next statement will only execute
after all of the threads have finished their work (otherwise your next
statement will see an undetermined intermediate view of the array).

> OK, I understand it better. But why does each {|t| t.join} join them
> all at the same time (ish), and not wait for the first one to finish
> executing before joining the others?


It joins them one at a time in order. But while your main thread is
waiting for a specific thread to finish, any other thread is also allowed
to execute, and possibly terminate. If thread b terminates while thread a
is joined, then you call join on thread b, join will return immediately
since there's nothing to wait for. Hence, each{|t| t.join} finishes
practically immediately when the longest running thread finishes.

--Ken

--
Ken (Chanoch) Bloom. PhD candidate. Linguistic Cognition Laboratory.
Department of Computer Science. Illinois Institute of Technology.
http://www.iit.edu/~kbloom1/
 
Reply With Quote
 
thefed
Guest
Posts: n/a
 
      12-31-2007

On Dec 31, 2007, at 11:15 AM, Robert Klemme wrote:

> On 31.12.2007 17:02, thefed wrote:


>> OK, I understand it better. But why does each {|t| t.join} join
>> them all at the same time (ish), and not wait for the first one
>> to finish executing before joining the others?

>
> They are not joined at the same time but one after the other.


But then why doesn't this take 15 seconds? t.join is called in the
main thread, so shouldn't the next Thread#join not get called until
the first one finishes?

module Enumerable
def each_simultaneously
threads = []
each { |e| threads >> Thread.new { yield e } }
return threads
end
end

start_time = Time.now
[7,8,9].each_simultaneously do |e|
sleep(5) # Simulate a long, high-latency operation
print "Completed operation for #{e}!\n"
end
# Completed operation for 8!
# Completed operation for 7!
# Completed operation for 9!
Time.now - start_time # => 5.009334

 
Reply With Quote
 
thefed
Guest
Posts: n/a
 
      12-31-2007
> module Enumerable
> def each_simultaneously
> threads = []
> each { |e| threads << Thread.new { yield e } }
> return threads
> end
> end


Sorry all, THIS is the fixed up version of each_simultaneously. Turns
out Ruby Cookbook has errors, too!

 
Reply With Quote
 
Craig Beck
Guest
Posts: n/a
 
      12-31-2007
>>> OK, I understand it better. But why does each {|t| t.join} join
>>> them all at the same time (ish), and not wait for the first one
>>> to finish executing before joining the others?

>>
>> They are not joined at the same time but one after the other.

>
> But then why doesn't this take 15 seconds? t.join is called in the
> main thread, so shouldn't the next Thread#join not get called until
> the first one finishes?
>
> module Enumerable
> def each_simultaneously
> threads = []
> each { |e| threads >> Thread.new { yield e } }
> return threads
> end
> end
>
> start_time = Time.now
> [7,8,9].each_simultaneously do |e|
> sleep(5) # Simulate a long, high-latency operation
> print "Completed operation for #{e}!\n"
> end
> # Completed operation for 8!
> # Completed operation for 7!
> # Completed operation for 9!
> Time.now - start_time # => 5.009334


try looking at the crude timeline below...

sec 0 1 2 3 4 5
6 7
|---------|---------|---------|---------|---------|---------|---------|
main ====@============================================= ====
t[1] ================================================== =
t[2] ================================================== =
t[3] ================================================== =

The @ on the main thread represents when the t.join gets called. It
waits in this simple case for t[1] to finish it's work (sleeping for 5
seconds), then waits for t[2]. As t[2] has also been doing work all
this time, it only blocks the main thread for another 0.1 sec before
finishing. Same for t[3]. So this contrived example it takes 5 seconds
+ whatever overhead for starting threads.

You could throw more instrumentation in there if you wish and do
things like adding additional calls to sleep to simulate extra thread
overhead to make it more obvious.

 
Reply With Quote
 
thefed
Guest
Posts: n/a
 
      12-31-2007

On Dec 31, 2007, at 3:46 PM, Craig Beck wrote:

> try looking at the crude timeline below...
>
> sec 0 1 2 3 4 5
> 6 7
> |---------|---------|---------|---------|---------|---------|--------
> -|
> main ====@============================================= ====
> t[1] ================================================== =
> t[2] ================================================== =
> t[3] ================================================== =
>
> The @ on the main thread represents when the t.join gets called. It
> waits in this simple case for t[1] to finish it's work (sleeping
> for 5 seconds), then waits for t[2]. As t[2] has also been doing
> work all this time, it only blocks the main thread for another 0.1
> sec before finishing. Same for t[3]. So this contrived example it
> takes 5 seconds + whatever overhead for starting threads.
>
> You could throw more instrumentation in there if you wish and do
> things like adding additional calls to sleep to simulate extra
> thread overhead to make it more obvious.


Thank you SO MUCH! This really clears threading up for me. In
retrospect it was less than obvious, but evident nonetheless. But
this timeline really made the difference for me. Thank you!


- Ari

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
why we should call Thread.start(),not directly call Thread.run()? junzhang1983@gmail.com Java 5 06-20-2008 03:12 PM
Why :: ? Why not : ? Why not . ? <- less clutter ?!? Skybuck Flying C++ 16 08-25-2007 09:48 PM
why why why why why Mr. SweatyFinger ASP .Net 4 12-21-2006 01:15 PM
findcontrol("PlaceHolderPrice") why why why why why why why why why why why Mr. SweatyFinger ASP .Net 2 12-02-2006 03:46 PM
why why why does function not work Horace Nunley ASP .Net 1 09-27-2006 09:52 PM



Advertisments