Hi,
I started on Ruby less than a week ago but have already come to
appreciate the power of the language. I am trying my hands on a classic
producer-consumer problem, implemented as an Orange tree (c.f. http://pine.fm/LearnToProgram/?Chapter=09). The Orange tree grows each
year until it dies and produces a random number of Oranges each year
(Producer). Oranges can be picked as long there are any on the tree
(Consumer).
I’ve got two problems here:
The attached code gives me the following exception:
/Users/Abhijit/Workspace/eclipse/ruby/learn_to_program/orange_tree.rb:84:
warning: instance variable @orange_tree not initialized
/Users/Abhijit/Workspace/eclipse/ruby/learn_to_program/orange_tree.rb:84:in <class:Worker>': undefined method age’ for nil:NilClass
(NoMethodError)
from
/Users/Abhijit/Workspace/eclipse/ruby/learn_to_program/orange_tree.rb:45:in
`’
I am not sure that the multithreading part is correctly coded.
Any help is very much appreciated. I’ve got myself a couple of books,
including “Programming Ruby” and “The Ruby P.ming Language” but
none of them contain a true “producer-consumer problem”.
The attached code gives me the following exception:
/Users/Abhijit/Workspace/eclipse/ruby/learn_to_program/orange_tree.rb:84:
warning: instance variable @orange_tree not initialized
/Users/Abhijit/Workspace/eclipse/ruby/learn_to_program/orange_tree.rb:84:in <class:Worker>': undefined methodage’ for nil:NilClass
(NoMethodError)
from
/Users/Abhijit/Workspace/eclipse/ruby/learn_to_program/orange_tree.rb:45:in
`’
Instance variables are only accessible from within methods. Lines
83-86 are not within a method. Take lines 83-86 out of the class
definition, and change them to something like:
Instance variables are only accessible from within methods. Lines
83-86 are not within a method. Take lines 83-86 out of the class
definition, and change them to something like:
Of course, variable orange_tree has to be visible from the outside, so
you have to add something like
attr_reader(:orange_tree)
at the beginning of the Worker class. This way you should not have
those exceptions anymore.
After making the changes you suggested and adding joins inside class
Worker, (file attached) I am now getting:
Orange picker going to sleep for 5
Orange picker woke up after sleeping for 5
Sorry, no Oranges to pick
Orange picker waiting patiently…
Age increaser going to sleep for 2
Age increaser woke up after sleeping for 2
/Users/Abhijit/Workspace/eclipse/ruby/learn_to_program/orange_tree.rb:76:in join': deadlock detected (fatal) from /Users/Abhijit/Workspace/eclipse/ruby/learn_to_program/orange_tree.rb:76:indo_some_work’
from
/Users/Abhijit/Workspace/eclipse/ruby/learn_to_program/orange_tree.rb:82:in <class:Worker>' from /Users/Abhijit/Workspace/eclipse/ruby/learn_to_program/orange_tree.rb:39:in’
That’s great. That’s the real problem the author wants you to solve. Sit
down with some paper and crayons / colored pencils and draw a picture of
the objects and what they have access to via your code. Then act out the
process. Can you understand how the deadlock occurs? What can you do to
avoid it?
If I were able to figure out the issue myself, why would I be sitting
here having this conversation? Do you see the problem? If yes, would you
be kind enough to point it out to me? If you do not see the problem
yourself, well, what can I say.
That’s not to say that I am going to sit with my thumb in my mouth while
someone else solves the problem for me. I am trying but a little help
goes a long way.
You might want to Google dining philosophers for another example.
I am very much aware of the Dining Philosophers example. It is not a
producer-consumer problem. It suggests a possible way to avoid deadlocks
which is to lock two shared resources in the same order in two competing
threads. I do not see the relevance with my issue here as I am not
dealing with two shared resources.
That’s great. That’s the real problem the author wants you to solve. Sit
down with some paper and crayons / colored pencils and draw a picture of
the objects and what they have access to via your code. Then act out the
process. Can you understand how the deadlock occurs? What can you do to
avoid it?
You might want to google dining philosophers for another example.
I rewrote the code using MonitorMixin and then back to using Mutex, same
result. I used 2 ConditionVariable, same result If it’s not working,
it’s certainly not for the lack of trying.
How much have you tried? Your description not showing that… You should
Google first… Then try those all. Let us know what have you tried and
what you got.
How much have you tried? Your description not showing that… You should
Google first… Then try those all. Let us know what have you tried and
what you got.
Look who’s talking the 1d10t trying to get even because he got
slapped on the wrist. Funny.
I rewrote the code using MonitorMixin and then back to using Mutex, same
result. I used 2 ConditionVariable, same result If it’s not working,
it’s certainly not for the lack of trying.
No one here who can slap me … before that he will get it.
No one said anything about slapping you, though you have it coming for
sure. It’s an English phrase which, as usual, you did not try to
understand by Googling. Why am I not surprised?
I am curious though, how will you hurt anyone in the forum? Are you
going to piss yourself in anger and expect them to have a heart attack
in content?
How much have you tried? Your description not showing that… You should
Google first… Then try those all. Let us know what have you tried and
what you got.
Look who’s talking the 1d10t trying to get even because he got
slapped on the wrist. Funny.
Don’t make me rude on you. don’t think yourself master on that. because
you didn’t invent Ruby.You are learning and me too. behave like an
educated guy.
No one here who can slap me … before that he will get it.
Entering Orange picker sync block…Entering age increaser sync block…
Entered age increaser sync block
Entered Orange picker sync block
C:/workspace/eclipse/ruby/learn_to_program/orange_tree.rb:108:in join': deadlock detected (fatal) from C:/workspace/eclipse/ruby/learn_to_program/orange_tree.rb:108:indo_some_work’
from C:/workspace/eclipse/ruby/learn_to_program/orange_tree.rb:116:in block in <class:Worker>' from C:/workspace/eclipse/ruby/learn_to_program/orange_tree.rb:115:intimes’
from C:/workspace/eclipse/ruby/learn_to_program/orange_tree.rb:115:in <class:Worker>' from C:/workspace/eclipse/ruby/learn_to_program/orange_tree.rb:45:in’
No one here who can slap me … before that he will get it.
No one said anything about slapping you, though you have it coming for
sure. It’s an English phrase which, as usual, you did not try to
understand by Googling. Why am I not surprised?
I am curious though, how will you hurt anyone in the forum? Are you
going to piss yourself in anger and expect them to have a heart attack
in content?
Don’t try to escape and you made that comments first. This is English
language forum. And I am least interested to do that. I don’t want to be
off topic.
On Sun, Feb 24, 2013 at 2:30 AM, Abhijit S. [email protected]
wrote:
Bump!
A problem unsolved keeps bugging me, I seriously hope the great minds
here would have something for me.
Just so we are sure we are all on the same level, can you please
repost the current version of the code? It may also help to get a
github account (free) and post all files in a single gist. That would
also allow for version tracking. It would also be help readability to
get rid of all the comments which contain alternative code.
it’s really not clear what you’re trying to do - a problem
definition would help.
as Robert pointed out, using Thread.abort_on_exception = true would
help. You would have found that Math does not have a rand method for
example.
the only shared state you need to protect access to is the @orange_count variable - it’s unnecessary to use synchronize around
the whole loop. Also, that logic would probably be better encapsulated
in the OrangeTree class itself.
you don’t check whether the exit condition for the orange_picker
thread is true before waiting on the CV. This will halt your thread
indefinitely when the age_increaser thread exits before the
orange_picker thread starts the wait. Multiple checking of conditions
is an unfortunate but necessary aspect of multi-threaded programming
with mutexes, etc.
lastly, before you go down the rabbit hole of mutexes and condition
variables, I would strongly advise you adopt the Actor model to
isolate access to state and use queues to communicate between threads.
You will save yourself a world of pain. BTW Tony’s advice to look at
DCell is good advice!
First of all, Sean’s modified version works!. It does not run into the
deadlock that mine does.
That being said, I’ll answer his questions below and, for newbies who
are facing similar problem like mine, also point out the difference that
made his code work and mine not.
Sean O’halpin wrote in post #1098770:
it’s really not clear what you’re trying to do - a problem
definition would help.
When I wrote this program about 3 weeks back, I was just starting to
learn Ruby and I picked on different aspects of the language that I
thought I needed practice on.
as Robert pointed out, using Thread.abort_on_exception = true would
help. You would have found that Math does not have a rand method for
example.
Lesson learnt, didn’t know. I guess this is one issue with interpreted
languages that you don’t see most problems until runtime.
the only shared state you need to protect access to is the @orange_count variable - it’s unnecessary to use synchronize around
the whole loop.
True. I thought then that @age also needs protection but on closer
review now, it doesn’t.
you don’t check whether the exit condition for the orange_picker
thread is true before waiting on the CV. This will halt your thread
indefinitely when the age_increaser thread exits before the
orange_picker thread starts the wait.
THIS was causing the deadlock. When I added just the check to my
program, it worked flawlessly. The missing check is the consequence of
(wrongly) thinking that @age is also protected by the sync block.
Thank you much for taking the time to troubleshoot the issue. I’d also
attempt a Monitor version of the same program later but for now I am
working on the Ruby Q. challenges.