How do threads and join work?


#1

I’m reading the pickaxe and it says on p137 “When a Ruby program
terminates, all threads are killed, regardless of their states.
However, you can wait for a particular thread to finish by calling
that thread’s Thread#join method. The calling thread will block until
the given thread is finished.”

I wrote a small program to see if I understood how this works.

running = true
t = Thread.new do
print “Thread started\n”
while(running); end
print “Thread finished\n”
end

t.join
puts “After join”
running = false
puts “Program finished”

The paragraph I quoted makes me think that it should print “Thread
started…After join…Thread finished…Program finished” It joins
the thread and continues processing, setting running to false, which
causes the thread’s loop to end. Also not that it could say “Program
finished…Thread finished”, I don’t think there’s a way to know for
sure.

Anyway, as you might guess, the actual output is “Thread started” and
it just hangs. I checked out the docs
(http://www.ruby-doc.org/core/classes/Thread.html#M001469) and it says
“Does not return until thr exits or until limit seconds have passed.”

That explains why it’s hanging - join is waiting for the thread to
exit. I’m confused as to the usefulness of threads in Ruby then. I
can see that if I were to create 5 threads, I can have them running
all at the same time, and then resume normal program execution.
However, more often I’d like to just perform some task in the
background while my program carries on as normal. In that case, a
Thread (to the extent that I know how to use it) is nothing more than
a method call.

Finally, I’ve tried putting Thread.pass inside the loop. Common sense
tells me it won’t work because join still hasn’t returned, but perhaps
Thread.pass is the answer if I’m not even supposed to be calling #join
in the first place.

I’d really appreciate some help in understanding this, and some
direction as to how to execute a task in the background.

Pat


#2

On Apr 23, 2006, at 11:06 am, Pat M. wrote:

puts “Program finished”

The paragraph I quoted makes me think that it should print “Thread
started…After join…Thread finished…Program finished” It joins
the thread and continues processing, setting running to false, which
causes the thread’s loop to end. Also not that it could say “Program
finished…Thread finished”, I don’t think there’s a way to know for
sure.

Your thread never actually finished, because you joined on it before
you set the value of running to false, so your code blocks at that
point. You need to swap the last lines like this:

running = true
t = Thread.new do
print “Thread started\n”
while(running); puts “running = #{running}”; sleep 1; end
puts “running = #{running}”;
print “Thread finished\n”
end

sleep 5

puts “Before join”
running = false
t.join
puts “After join”
puts “Program finished”

I added two things into the while loop: a sleep so you don’t run your
CPU at 100% and a ticker.

Ashley


#3

puts “Before join”
running = false
t.join
puts “After join”
puts “Program finished”

Actually I just noticed that in my version the join is redundant. I
only just woke up :slight_smile:

You would need join if you wrote something like this:

running = true
t = Thread.new do
print “Thread started\n”
#do_something
sleep 10
#do_something_else
print “Thread finished\n”
end

puts “Before join”
t.join
puts “After join”
puts “Program finished”

Hope that clears it up

Ashley


#4

I’ve been using these two lines to capture text that only appears once,
but
the second line bothers me a little. Is there a one-line idiom for this
that extracts groups like #scan but returns a string instead of an array
so
the second line isn’t needed?

text = string.scan( %r{ skip_this ( keep_this ) skip_this }is )
text = text[ 0 ]

Thanks for any help,
Chris


#5

On 4/23/06, Ashley M. removed_email_address@domain.invalid wrote:

t.join

Your thread never actually finished, because you joined on it before
you set the value of running to false, so your code blocks at that
point.

Right, I mentioned that in my OP. I want to know if there’s a way to
get join-like behavior - do not terminate the program until that
thread has finished executing - without blocking.

sleep 5

puts “Before join”
running = false
t.join
puts “After join”
puts “Program finished”

I added two things into the while loop: a sleep so you don’t run your
CPU at 100% and a ticker.

This gives the output I want, but doesn’t actually perform what I
want. The ultimate goal here is to basically tell my program “Go
start doing this in the background, and I’m going to keep working.”

Pat


#6

Well, now, that’s just embarassing. Thanks!

----- Original Message -----
From: “Pawel S.” removed_email_address@domain.invalid
To: “ruby-talk ML” removed_email_address@domain.invalid
Sent: Sunday, April 23, 2006 8:28 AM
Subject: Re: Scan-like Idiom to Capture a Group as a String Instead of
an
Array?


#7

On 4/23/06, clc removed_email_address@domain.invalid wrote:

I’ve been using these two lines to capture text that only appears once, but
the second line bothers me a little. Is there a one-line idiom for this
that extracts groups like #scan but returns a string instead of an array so
the second line isn’t needed?

text = string.scan( %r{ skip_this ( keep_this ) skip_this }is )
text = text[ 0 ]

Any reason why

text = string.scan( %r{ skip_this ( keep_this ) skip_this }is ).first

or

text = string.scan( %r{ skip_this ( keep_this ) skip_this }is )[0]

wouldn’t work?

-Pawel


#8

Hi –

On Sun, 23 Apr 2006, clc wrote:

Well, now, that’s just embarassing. Thanks!

I might have misunderstood your original question, but if you’re happy
with a one-element array rather than a string, you could also do:

text, = str.scan(/…/)

David


David A. Black (removed_email_address@domain.invalid)
Ruby Power and Light, LLC (http://www.rubypowerandlight.com)

“Ruby for Rails” PDF now on sale! http://www.manning.com/black
Paper version coming in early May!


#9

Hi –

On Sun, 23 Apr 2006, Pawel S. wrote:

text = string.scan( %r{ skip_this ( keep_this ) skip_this }is ).first

or

text = string.scan( %r{ skip_this ( keep_this ) skip_this }is )[0]

wouldn’t work?

They’ll do the same thing as Chris’s example, though they all return
arrays rather than strings:

“abc”.scan(/a(b)c/)[0]
=> [“b”]

so a further extraction would be necessary.

David


David A. Black (removed_email_address@domain.invalid)
Ruby Power and Light, LLC (http://www.rubypowerandlight.com)

“Ruby for Rails” PDF now on sale! http://www.manning.com/black
Paper version coming in early May!


#10

Oh, I didn’t realize you could do that with parallel assignments too.
Thanks for the help.

----- Original Message -----
From: removed_email_address@domain.invalid
To: “ruby-talk ML” removed_email_address@domain.invalid
Sent: Sunday, April 23, 2006 8:52 AM
Subject: Re: Scan-like Idiom to Capture a Group as a String Instead of
an
Array?


#11

clc wrote:

text = string[/skip_this (keep_this) skip_this}is/, 1]

and if you don’t need the skip_this,

text = string[/keep_this/]

Yoann


#12

On Apr 23, 2006, at 7:21 AM, clc wrote:

I’ve been using these two lines to capture text that only appears
once, but the second line bothers me a little. Is there a one-line
idiom for this that extracts groups like #scan but returns a string
instead of an array so the second line isn’t needed?

text = string.scan( %r{ skip_this ( keep_this ) skip_this }is )
text = text[ 0 ]

If I understood the question correctly, here are my best ideas:

str = “skip this find this skip this”
=> “skip this find this skip this”

found = str.match(/skip this (find this) skip this/)[1]
=> “find this”

another_found = str[/find this/]
=> “find this”

yet_another_found = str[/skip this (find this) skip this/, 1]
=> “find this”

Hope that helps.

James Edward G. II


#13

James Edward G. II wrote:

another_found = str[/find this/]
=> “find this”

yet_another_found = str[/skip this (find this) skip this/, 1]
=> “find this”

arg, sorry for the dupe.

Yoann


#14

2006/4/23, clc removed_email_address@domain.invalid:

I’ve been using these two lines to capture text that only appears once, but
the second line bothers me a little. Is there a one-line idiom for this
that extracts groups like #scan but returns a string instead of an array so
the second line isn’t needed?

text = string.scan( %r{ skip_this ( keep_this ) skip_this }is )
text = text[ 0 ]

Adding to all the other replies: keep in mind that scan usually
returns multiple results which implies that you want to process all of
them.

If you know that there will be only one match of this in your string
or if you only want to work with the first match, I suggest to use =~
or String[%r{ skip_this ( keep_this ) skip_this }is, 1] as Yoann
showed.

Kind regards

robert


#15

Thanks for clarifying the operation of scan – I mistakenly thought I
was
getting a string when I was actually getting an array, so my original
question wasn’t even correct. Anyway, all of the methods listed below
seem
to work, and the last one seems especially elegant.

Thanks for the advice!
Chris

line = “name: value”

value = line.scan(%r{name: ([[:alnum:]]+)}i)[0][0]
puts “#{value.class}: #{value}”

value = line.scan(%r{name: ([[:alnum:]]+)}i).first.first
puts “#{value.class}: #{value}”

value = line.match(%r{name: ([[:alnum:]]+)}i)[1]
puts “#{value.class}: #{value}”

value = line[%r{name: ([[:alnum:]]+)}i, 1]
puts “#{value.class}: #{value}”


#16

On Apr 23, 2006, at 7:35 AM, Pat M. wrote:

Right, I mentioned that in my OP. I want to know if there’s a way to
get join-like behavior - do not terminate the program until that
thread has finished executing - without blocking.

Have you considered the possibility that you’re either a) doing
things in the wrong order or b) have your infinite loop in the wrong
thread.

You can just make #join the last thing you do, that’s effectively
“non-blocking”

e.g.

t = Thread.start { … }

do other stuff in main thread

t.join # no code follows this anyway

Or if the thread should drive the running of the other stuff:

require ‘mutex’
m = Mutex.new
is_running = true

t = Thread.start do
while getting_stuff_or_whatever
do_stuff( )
end
# we stopped getting stuff
m.synchronize { is_running = false }
end

local_running = nil
m.synchronize { local_running = is_running }

while local_running
do_other_stuff
m.synchronize do
unless is_running
local_running = false
end
end
end

Of course this example is silly you should probably use a queue