Forum: Ruby How do threads and join work?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Pat M. (Guest)
on 2006-04-23 14:07
(Received via mailing list)
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
Ashley M. (Guest)
on 2006-04-23 15:27
(Received via mailing list)
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
Ashley M. (Guest)
on 2006-04-23 15:33
(Received via mailing list)
>   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 :)

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
Pat M. (Guest)
on 2006-04-23 15:36
(Received via mailing list)
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
clc (Guest)
on 2006-04-23 16:22
(Received via mailing list)
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
Pawel S. (Guest)
on 2006-04-23 16:32
(Received via mailing list)
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
clc (Guest)
on 2006-04-23 16:38
(Received via mailing list)
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?
unknown (Guest)
on 2006-04-23 16:38
(Received via mailing list)
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!
unknown (Guest)
on 2006-04-23 16:53
(Received via mailing list)
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!
clc (Guest)
on 2006-04-23 17:31
(Received via mailing list)
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?
James G. (Guest)
on 2006-04-23 18:44
(Received via mailing list)
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
Yoann G. (Guest)
on 2006-04-24 15:13
(Received via mailing list)
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
Yoann G. (Guest)
on 2006-04-24 15:16
(Received via mailing list)
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
Robert K. (Guest)
on 2006-04-24 16:15
(Received via mailing list)
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
clc (Guest)
on 2006-04-24 19:31
(Received via mailing list)
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}"
Logan C. (Guest)
on 2006-04-24 20:21
(Received via mailing list)
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
This topic is locked and can not be replied to.