Thread.start(variables) do question

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?

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

On Sun, Dec 30, 2012 at 2:38 AM, Grant S. [email protected]
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

On 30 December 2012 21:21, Robert K. [email protected]
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 K., 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

On 30 December 2012 22:43, Matthew K. [email protected] 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 K., 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

On Sun, Dec 30, 2012 at 2:09 PM, Matthew K. [email protected]
wrote:

On 30 December 2012 22:43, Matthew K. [email protected] wrote:

On 30 December 2012 21:21, Robert K. [email protected]
wrote:

On Sun, Dec 30, 2012 at 2:38 AM, Grant S. [email protected]
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

On Sun, 30 Dec 2012 13:43:53 +0100, Matthew K.
[email protected] 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.)

Matthew K. 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?

7stud – [email protected] 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 R. [email protected] 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 K., 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