Pages 133-5: Mutual exclusion - Monitors
Listen to this article.
Value: High
Level: Hard
Summary:
When you work with shared data, race conditions can occur. For example, the simple +=() operator breaks down into 3 different operations within the Ruby interpreter (load, add, store). If different threads are using this operator on some shared data, race conditions could obviously occur. To avoid these bugs you can use mutual exclusion, in form of several secure synchronization schemes. You could use Thread.critical=(), the Sync library, the Mutex_m library, the Queue class or the Monitor library. Monitors wrap an object containing some kind of resource with synchronization functions. You can protect a block using Monitor#synchronize(): the code within this block will be accessible only to one thread at a time for any particular monitor object.
Memo: Use mutual exclusion when accessing and writing shared data in a multithreaded program.
Example:
# Bugged code
class Counter
attr_reader :count
def initialize
@count = 0
super
end
def tick
@count += 1
end
end
c = Counter.new
t1 = Thread.new { 10000.times { c.tick } }
t2 = Thread.new { 10000.times { c.tick } }
t1.join
t2.join
c.count
# Debugged code
require 'monitor'
class Counter < Monitor
attr_reader :count
def initialize
@count = 0
super
end
def tick
synchronize do
@count += 1
end
end
end
c = Counter.new
t1 = Thread.new { 10000.times { c.tick } }
t2 = Thread.new { 10000.times { c.tick } }
t1.join; t2.join
c.count
Reported errata (at 10/17/06 14:17:18 PDT): 1
* Here there's the full text of erratum #2092, and the author's answer: "The code print out shows c.count as being 20000, while the text says that it shouldn't be. The script itself seems fine as running it yields values other than 20000. (Dave says: yeah... they changed the thread scheduler, so both threads now run to completion. I've upped the count) —Justin"
Errata I found: 3
* On page 135, line 14 of table 11.1 states "t2: add 1 to val 4". It should be "t2: add 1 to val 3".
* On page 134 I read "Perhaps surprisingly, the count doesn't equal 20,000". I couldn't verify this statement. Nearly 50% of the times I ran the code I got 20.000. This is what reported erratum #2092 is about. Answering erratum #2092, the author states that now the count equals 20.000, and that he updated it in a new version of the book. Unfortunately running the code I got values other than 20.000 nearly 50% of the times.
* On page 792, in the index, method super is associated to page 554. Unfotunately, on page 554 there's nothing about method super.
My suggestions to the author: 1 (5)
* (See doubts 1-5)
Doubts: 5 (10)
* On page 134, line 5 of the code calls the method super.
o Why ?
o What does it exactly do in this particular case ?
o Why does the code run the same with or without it ?
o Which class/module does super method belong ?
* Why does the count not always equal the same number ?
o Why does the count sometime equal 20.000 even if the code is bugged ?
o Why does the count not always equal 20.000, even if "they changed the thread scheduler" (Dave Thomas, answer to reported erratum #2092) ?
* What is the rule behind race conditions ?
* Running the code on page 134 in my IRb (see below, blank lines abridged), I once got a count equal to -517607059. Why ?
irb(main):113:0> class Counter
irb(main):114:1> attr_reader :count
irb(main):115:1> def initialize
irb(main):116:2> @count = 0
irb(main):117:2> super
irb(main):118:2> end
irb(main):119:1> def tick
irb(main):120:2> @count += 1
irb(main):121:2> end
irb(main):122:1> end
=> nil
irb(main):123:0> c = Counter.new
=> #
irb(main):124:0> t1 = Thread.new { 10000.times { c.tick } }
=> #
irb(main):125:0> t2 = Thread.new { 10000.times { c.tick } }
=> #
irb(main):126:0> t1.join
=> #
irb(main):127:0> t2.join
=> #
irb(main):128:0> c.count
=> -517607059
* On page 135, class Counter is set as subclass of "Monitor".
o Is monitor also a Ruby built-in class, or just a library you can require in your programs ?
o Why is class Counter set as subclass of "Monitor" ?
o If I want to create a monitor object, do I need to both require monitor library and make my object's class a sublcass of class Monitor or not ?
Level: Hard
Summary:
When you work with shared data, race conditions can occur. For example, the simple +=() operator breaks down into 3 different operations within the Ruby interpreter (load, add, store). If different threads are using this operator on some shared data, race conditions could obviously occur. To avoid these bugs you can use mutual exclusion, in form of several secure synchronization schemes. You could use Thread.critical=(), the Sync library, the Mutex_m library, the Queue class or the Monitor library. Monitors wrap an object containing some kind of resource with synchronization functions. You can protect a block using Monitor#synchronize(): the code within this block will be accessible only to one thread at a time for any particular monitor object.
Memo: Use mutual exclusion when accessing and writing shared data in a multithreaded program.
Example:
# Bugged code
class Counter
attr_reader :count
def initialize
@count = 0
super
end
def tick
@count += 1
end
end
c = Counter.new
t1 = Thread.new { 10000.times { c.tick } }
t2 = Thread.new { 10000.times { c.tick } }
t1.join
t2.join
c.count
# Debugged code
require 'monitor'
class Counter < Monitor
attr_reader :count
def initialize
@count = 0
super
end
def tick
synchronize do
@count += 1
end
end
end
c = Counter.new
t1 = Thread.new { 10000.times { c.tick } }
t2 = Thread.new { 10000.times { c.tick } }
t1.join; t2.join
c.count
Reported errata (at 10/17/06 14:17:18 PDT): 1
* Here there's the full text of erratum #2092, and the author's answer: "The code print out shows c.count as being 20000, while the text says that it shouldn't be. The script itself seems fine as running it yields values other than 20000. (Dave says: yeah... they changed the thread scheduler, so both threads now run to completion. I've upped the count) —Justin"
Errata I found: 3
* On page 135, line 14 of table 11.1 states "t2: add 1 to val 4". It should be "t2: add 1 to val 3".
* On page 134 I read "Perhaps surprisingly, the count doesn't equal 20,000". I couldn't verify this statement. Nearly 50% of the times I ran the code I got 20.000. This is what reported erratum #2092 is about. Answering erratum #2092, the author states that now the count equals 20.000, and that he updated it in a new version of the book. Unfortunately running the code I got values other than 20.000 nearly 50% of the times.
* On page 792, in the index, method super is associated to page 554. Unfotunately, on page 554 there's nothing about method super.
My suggestions to the author: 1 (5)
* (See doubts 1-5)
Doubts: 5 (10)
* On page 134, line 5 of the code calls the method super.
o Why ?
o What does it exactly do in this particular case ?
o Why does the code run the same with or without it ?
o Which class/module does super method belong ?
* Why does the count not always equal the same number ?
o Why does the count sometime equal 20.000 even if the code is bugged ?
o Why does the count not always equal 20.000, even if "they changed the thread scheduler" (Dave Thomas, answer to reported erratum #2092) ?
* What is the rule behind race conditions ?
* Running the code on page 134 in my IRb (see below, blank lines abridged), I once got a count equal to -517607059. Why ?
irb(main):113:0> class Counter
irb(main):114:1> attr_reader :count
irb(main):115:1> def initialize
irb(main):116:2> @count = 0
irb(main):117:2> super
irb(main):118:2> end
irb(main):119:1> def tick
irb(main):120:2> @count += 1
irb(main):121:2> end
irb(main):122:1> end
=> nil
irb(main):123:0> c = Counter.new
=> #
irb(main):124:0> t1 = Thread.new { 10000.times { c.tick } }
=> #
irb(main):125:0> t2 = Thread.new { 10000.times { c.tick } }
=> #
irb(main):126:0> t1.join
=> #
irb(main):127:0> t2.join
=> #
irb(main):128:0> c.count
=> -517607059
* On page 135, class Counter is set as subclass of "Monitor".
o Is monitor also a Ruby built-in class, or just a library you can require in your programs ?
o Why is class Counter set as subclass of "Monitor" ?
o If I want to create a monitor object, do I need to both require monitor library and make my object's class a sublcass of class Monitor or not ?
0 Comments:
Post a Comment
<< Home