Forum: Ruby Thread.start(variables) do question

Posted by Grant Schoep (matobinder)
on 2012-12-30 02:37
So I have been looking at some code I inherited. I see two examples of
thread use in this. Wondering what the difference is. I've made up two
examples that show this, wondering what the difference is, and why to
use one or the other.

---Example 1-----------------------------------------------------------
obj1 = SomeObject.new()
obj2 = SomeOtherObject.new()
obj3 = "helloworld"

Thread.start(obj1, obj2, myString) do |thing1, thing2, thing3|
  #use the variables named thing1, thing2, thing3.
end
-----------------------------------------------------------------------

And then this way...
---Example 2-----------------------------------------------------------
obj1 = SomeObject.new()
obj2 = SomeOtherObject.new()
obj3 = "helloworld"

Thread.start() do
  #use the objects named obj1, obj2, obj3
end
-----------------------------------------------------------------------

So, What is the reason for doing Example 1 versus Example 2. The 2nd
example seems simpler in my book.  My guess is it has something to do
with how the objects may be used outside the thread?
Posted by 7stud -- (7stud)
on 2012-12-30 03:54
x = 10

t = Thread.start do
  x = 20
  puts "In thread, x's value is: %s" % x
end

t.join

puts "In main thread, x's value is: #{x}"

--output:--
In thread, x's value is: 20
In main thread, x's value is: 20




================




x = 10

t = Thread.start(x) do |y|
  y = 20
  puts "In thread, y's value is: %s" % y
end

t.join

puts "In main thread, x's value is: #{x}"


--output:--
In thread, y's value is: 20
In main thread, x's value is: 10


===========


However,



x = 'hello'

t = Thread.start(x) do |y|
  y << ' world'
  puts "In thread, y's value is: %s" % y
end

t.join

puts "In main thread, x's value is: #{x}"


--output:--
In thread, y's value is: hello world
In main thread, x's value is: hello world
Posted by Robert Klemme (robert_k78)
on 2012-12-30 12:22
(Received via mailing list)
On Sun, Dec 30, 2012 at 2:38 AM, Grant Schoep <lists@ruby-forum.com> 
wrote:
> So, What is the reason for doing Example 1 versus Example 2. The 2nd
> example seems simpler in my book.  My guess is it has something to do
> with how the objects may be used outside the thread?

See 7stud's examples.  It's not about objects but about scope of 
variables.

Here's another way to demonstrate the effect:

$ ruby -e 'for i in 0..4; Thread.new(i) {|n| sleep 1;p [i,n]} end;sleep 
2'
[4, 3]
[4, 4]
[4, 2]
[4, 1]
[4, 0]

Kind regards

robert
Posted by Matthew Kerwin (mattyk)
on 2012-12-30 13:44
(Received via mailing list)
On 30 December 2012 21:21, Robert Klemme <shortcutter@googlemail.com> 
wrote:

> $ ruby -e 'for i in 0..4; Thread.new(i) {|n| sleep 1;p [i,n]} end;sleep 2'
> [4, 3]
> [4, 4]
> [4, 2]
> [4, 1]
> [4, 0]
>
> Kind regards
>
> robert
>

So does that mean these are functionally identical?

    Thread.new(a) { |b| do_something_with b }

.. and ..

    Thread.new { b=a; do_something_with b }

Or is there a potential race condition caused by possibly deferred
execution of the block, which is avoided by performing the "renaming"
assignment in the parent thread?  I'm trying to think of an example, but 
am
having trouble.

--
  Matthew Kerwin, B.Sc (CompSci) (Hons)
  http://matthew.kerwin.net.au/
  ABN: 59-013-727-651

  "You'll never find a programming language that frees
  you from the burden of clarifying your ideas." - xkcd
Posted by Matthew Kerwin (mattyk)
on 2012-12-30 14:10
(Received via mailing list)
On 30 December 2012 22:43, Matthew Kerwin <matthew@kerwin.net.au> wrote:

>>
>>
>
> Or is there a potential race condition caused by possibly deferred
> execution of the block, which is avoided by performing the "renaming"
> assignment in the parent thread?  I'm trying to think of an example, but am
> having trouble.
>

Thought of one (sorry for replying to myself):

    a = 1
    loop do
      Thread.new { b=a; p b }
      a += 1
    end

.. where `a += 1` is likely to run before `b=a`; as opposed to ..

    a = 1
    loop do
      Thread.new(a) { |b| p b }
      a += 1
    end

.. where passing the value of _a_ to Thread#new is guaranteed to happen
before `a += 1`, so _b_ will always have the right value.

--
  Matthew Kerwin, B.Sc (CompSci) (Hons)
  http://matthew.kerwin.net.au/
  ABN: 59-013-727-651

  "You'll never find a programming language that frees
  you from the burden of clarifying your ideas." - xkcd
Posted by Bartosz DziewoƄski (matmarex)
on 2012-12-30 14:15
(Received via mailing list)
On Sun, 30 Dec 2012 13:43:53 +0100, Matthew Kerwin 
<matthew@kerwin.net.au> wrote:
> assignment in the parent thread?  I'm trying to think of an example, but am
> having trouble.

There's another thing apart from the race conditions. These are mostly 
equivalent (except for what you've already said) only if there is no 
variable called `b` in the outer scope - if there was one, in your 
second example `b=a` will assign to it and change the value outside the 
thread. In the first case, the "outer" `b` will simple be inaccessible 
inside the thread, but its value outside the thread won't be changed.

(The above only applies to Ruby 1.9 and newer, the semantics for both 
versions on 1.8 are the same - if there is an "outer" b, its value will 
change. This is obviously not always what the coder has intended and so 
it was changed in 1.9.)
Posted by Robert Klemme (robert_k78)
on 2012-12-30 21:12
(Received via mailing list)
On Sun, Dec 30, 2012 at 2:09 PM, Matthew Kerwin <matthew@kerwin.net.au> 
wrote:
> On 30 December 2012 22:43, Matthew Kerwin <matthew@kerwin.net.au> wrote:
>>
>> On 30 December 2012 21:21, Robert Klemme <shortcutter@googlemail.com>
>> wrote:
>>>
>>> On Sun, Dec 30, 2012 at 2:38 AM, Grant Schoep <lists@ruby-forum.com>
>>> wrote:

>>> Here's another way to demonstrate the effect:
>>>
>>> $ ruby -e 'for i in 0..4; Thread.new(i) {|n| sleep 1;p [i,n]} end;sleep
>>> 2'
>>> [4, 3]
>>> [4, 4]
>>> [4, 2]
>>> [4, 1]
>>> [4, 0]

>       a += 1
> .. where passing the value of _a_ to Thread#new is guaranteed to happen
> before `a += 1`, so _b_ will always have the right value.

This race condition is what my example posted earlier demonstrates.
You can change it a bit to make it more similar to your a+=1 example:

$ ruby -e 'a=0; 4.times {Thread.new(a) {|n| sleep 1;p [a,n]}; a+=1}; 
sleep 2'
[4, 1]
[4, 0]
[4, 2]
[4, 3]

Kind regards

robert
Posted by 7stud -- (7stud)
on 2012-12-30 22:01
Matthew Kerwin wrote in post #1090641:
>
> Thought of one (sorry for replying to myself):
>
>     a = 1
>     loop do
>       Thread.new { b=a; p b }
>       a += 1
>     end
>
> .. where `a += 1` is likely to run before `b=a`;

What makes you think that?
Posted by Matthew Kerwin (mattyk)
on 2012-12-31 00:27
(Received via mailing list)
7stud -- <lists@ruby-forum.com> wrote:

> > .. where `a += 1` is likely to run before `b=a`;
>
> What makes you think that?


Experience in multithreaded environments. Without an explicit mutex or
other locking mechanism (or call to Thread#join) there's no guarantee 
that
either will run before the other.  I know that in MRI the GVL is a large
player, but I'm not being implementation-specific here.

If I really had to guess, in a truly parallel multithreaded environment,
assuming that the block begins execution at the same instant as the line
after Thread.new, I'd guess that the `b=a` operation would happen before
the `a = ...`, assuming that `a += 1` is actually broken down into `a = 
a +
1` which has an extra operation and so should take longer. However in a
less parallel system I would have assumed the current thread would 
execute
a bit before the child thread was given some CPU time, so `a += 1` would
have a chance to complete before `b=a`. In either case it's something 
over
which I as the programmer do not have any control.

Matma Rex <matma.rex@gmail.com> wrote:
> There's another thing apart from the race conditions. These are mostly
equivalent (except for what you've already said) only if there is no
variable called `b` in the outer scope - if there was one, in your 
second
example `b=a` will assign to it and change the value outside the thread. 
In
the first case, the "outer" `b` will simple be inaccessible inside the
thread, but its value outside the thread won't be changed.

Ah, that's a really good point. I'd forgotten that 1.9 masks variables 
with
the same name as block parameters. Now that you've said it, I remember
having to refactor a bunch of OptParse code that used to use:

    on('-x', Integer, 'foobar') {|$x| }

--
  Matthew Kerwin, B.Sc (CompSci) (Hons)
  http://matthew.kerwin.net.au/
  ABN: 59-013-727-651

  "You'll never find a programming language that frees
  you from the burden of clarifying your ideas." - xkcd
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.