This week's book giveaway is in the Kotlin forum.
We're giving away four copies of Kotlin in Action and have Dmitry Jemerov & Svetlana Isakova on-line!
See this thread for details.
Win a copy of Kotlin in Action this week in the Kotlin forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic

Producer Consumer with limited content of unknown size.  RSS feed

 
Roy Wood
Greenhorn
Posts: 7
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I have a producer/consumer problem.

My Producer will not be producing endlessly. (can't use while(true)) It will work on some input and place items onto a queue continually until it's input is exhausted. The size of this input can vary.
My Consumer will not be consuming endlessly. (can't use while(true)) It will consume from the queue for as long as the queue has items in it OR the producer has not yet terminated.

How can I get my Consumer to know that the Producer thread has terminated?
My Solution was to have the main method create the producer thread and start it. Then create the consumer thread and pass into it the producer that was created and start the thread. The producer would have a flag it sets when it completes. The consumer would loop, checking both if the queue was empty and checking to see if the producer had set it's flag. If the queue is not empty OR the flag is not set, continue looping, trying to take stuff off the queue.

This was deemed not acceptable. Passing the producer into the consumer was bad.

How can I get around this and have the consumer know when the producer has finished?
 
Tushar Goel
Ranch Hand
Posts: 934
4
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
How are you creating producers/consumers thread? Is this some home assignment?

 
Roy Wood
Greenhorn
Posts: 7
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Tushar Goel wrote:How are you creating producers/consumers thread? Is this some home assignment?



I am using ExecutorService to create my threads.

This is work, not homework. As a result I can't post any code, only "describe" the nature of my problem. My work was code reviewed and this problem was flagged up.
 
Mike. J. Thompson
Bartender
Posts: 689
17
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I'm a little confused about what exactly your requirements are here. Your producer is putting work into the queue, but you don't want the consumer to process that work once the producer has run out of input?

Well in order to do this there will need to be some form of communication between the two threads. The consumer will maintain a flag indicating whether it should continue, and the producer will set the flag to false when it is done.

You may want to look into the listener pattern that is used by graphical components in Java. You could have an interface called InputExhaustedListener. The producer will allow listeners to be registered with it, and will trigger all listeners when its input is exhausted. The creator of the threads can then register a listener with the producer that will stop the consumer when the input is exhausted.

However I think you may have a problem if your consumer quits when the queue is empty. That will create a race condition, and the consumer could exit at any time even if the producer is still running. If at any time the consumer is running faster than the producer it could empty its queue.
 
Tim Cooke
Marshal
Posts: 3858
233
Clojure IntelliJ IDE Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
The general pattern for producer/consumer queues means that the producer and consumer don't have any programmatic link between them because they are often physically separated with communication over a network. So programmatic communication is not often feasible.

One solution is to enhance your message protocol to include a control message that will instruct your consumer to terminate.
 
Roy Wood
Greenhorn
Posts: 7
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Mike. J. Thompson wrote:I'm a little confused about what exactly your requirements are here. Your producer is putting work into the queue, but you don't want the consumer to process that work once the producer has run out of input?

Well in order to do this there will need to be some form of communication between the two threads. The consumer will maintain a flag indicating whether it should continue, and the producer will set the flag to false when it is done.

You may want to look into the listener pattern that is used by graphical components in Java. You could have an interface called InputExhaustedListener. The producer will allow listeners to be registered with it, and will trigger all listeners when its input is exhausted. The creator of the threads can then register a listener with the producer that will stop the consumer when the input is exhausted.

However I think you may have a problem if your consumer quits when the queue is empty. That will create a race condition, and the consumer could exit at any time even if the producer is still running. If at any time the consumer is running faster than the producer it could empty its queue.


The Consumer has to continue processing content in the queue if there is some there. Always. However it could be that the queue is empty but the producer hasn't finished producing, so we need the consumer to not see an empty queue as being a signal to stop. The producer could also finish while there is stuff on the queue so again the consumer needs to continue. only when both the queue is empty AND the producer has finished, should the consumer also finish. Both conditions (queue size and producer flag) are checked by the consumer because, agreed, we get all sorts of mess if we don't.
Hope I explained that well.

Listener sounds interesting
 
Tim Cooke
Marshal
Posts: 3858
233
Clojure IntelliJ IDE Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Tim Cooke wrote:One solution is to enhance your message protocol to include a control message that will instruct your consumer to terminate.

This will only work if your queue is of type FIFO. Otherwise your consumer could pick up the control message and terminate before processing all other messages.
 
Roy Wood
Greenhorn
Posts: 7
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Tim Cooke wrote:
Tim Cooke wrote:One solution is to enhance your message protocol to include a control message that will instruct your consumer to terminate.

This will only work if your queue is of type FIFO. Otherwise your consumer could pick up the control message and terminate before processing all other messages.

I can confirm that the queue is FIFO but yes, that would be a pitfall if it weren't.

As for keeping both processes completely separate. That would be fine if not for the fact that we don't want either process to run endlessly. The Consumer needs to stop when there is no more work for it to do. We need a means to tell it that there is no more work to do. The only way to do that is to tell if the thing creating the work, has stopped creating more work.

Edit: I like the idea of a control message however the items on the queue are custom objects, not strings and not objects that I am creating. I could use null as a signal but that sounds like it could have problems....
 
Tim Cooke
Marshal
Posts: 3858
233
Clojure IntelliJ IDE Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Using the control message idea, here's how it works:

Producer

Consumer

This also assumes a single producer, single consumer configuration.
 
Campbell Ritchie
Marshal
Posts: 55678
161
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Welcome to the Ranch

This question is too difficult for “beginning”, so I shall move it. [edit]Have also duplicated this discussion in our synchronisation forum in the hope of getting more attention for it.[/edit]

Are you aware of FIFO collections which block when they are empty? Have a look in the Java® Tutorials, and I think you might want some sort of blocking queue.
 
Campbell Ritchie
Marshal
Posts: 55678
161
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Roy Wood wrote: . . . I could use null as a signal but that sounds like it could have problems....
Possibly, but not with blocking queues; it says they don't accept nulls.
 
Tim Cooke
Marshal
Posts: 3858
233
Clojure IntelliJ IDE Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Campbell's comment raises another question. What exactly is the queue? Is it a programmatic queue, i.e. some implementation of the Java Queue interface? Or is it something else, like Tibco, JMS, RabbitMQ, other?
 
Roy Wood
Greenhorn
Posts: 7
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Campbell Ritchie wrote:
Roy Wood wrote: . . . I could use null as a signal but that sounds like it could have problems....
Possibly, but not with blocking queues; it says they don't accept nulls.


Yes, I am using a standard Java Utils BlockingQueue. I didn't know about the null restriction but I suppose it makes sense. Definitely spot on about messages only working if there is only 1 producer and 1 consumer. I am currently only using 1 producer and 1 consumer but I don't want to limit myself just in case requirements change. (requirements always seem to change, /cynic)

Thank you for the welcome. I definitely still feel like a beginner. That's why I posted there!
 
Campbell Ritchie
Marshal
Posts: 55678
161
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
It says no nulls just under the table of methods on this page, but it uses nulls as sentinel values.
 
Chan Ag
Rancher
Posts: 1090
14
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
http://www.coderanch.com/t/614729/threads/java/Producer-Consumer-Thread

The above mentioned topic has a response ( I don't know how to link the specific post within a thread ) that has earned a cow. It has a really nice explanation about these control messages and the little things we must consider while setting up such a communication.

Personally I like the idea of a PoisonPill ( as suggested in the linked response ). But whether it can be used depends upon your project implementation.
 
Chan Ag
Rancher
Posts: 1090
14
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
And if you happen to use the PoisonPill ( make it static and final ) approach, have your consumers put the PoisonPill back into the queue, so the other consumers also get to know that the producer has terminated.
 
Roy Wood
Greenhorn
Posts: 7
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi Chan

The poison pill does seem like a good idea. Just because the objects going onto the queue aren't being made by me, it doesn't mean that the poison pill can't be made by me.
As for putting the pill back on the queue. I only have one consumer but it's good practice to do just in case something changes in future. Thanks.
 
Tim Cooke
Marshal
Posts: 3858
233
Clojure IntelliJ IDE Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Roy Wood wrote:I am currently only using 1 producer and 1 consumer but I don't want to limit myself just in case requirements change.

I don't recommend this at all. If your requirement is for a single producer and a single consumer, then design your solution for that requirement. Dealing with the possibility of multiple producers and consumers introduces significant complexity. If you don't have to deal with it, then don't.

If the requirement changes in the future, then that's when you rethink your design, in the future. Remember, you may never have to.
 
Tushar Goel
Ranch Hand
Posts: 934
4
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Well, blocking queue also provides some functionality to wait for an element to be available with out returning null in case of queue is empty.
You can also use some time limited functionality "poll" to wait for particular time period to avoid long waiting.
 
Campbell Ritchie
Marshal
Posts: 55678
161
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Chan Ag wrote: . . . I don't know how to link the specific post within a thread . . .
You right‑click the icon at the top left of the thread next to the date posted, then copy link location, paste it into the URL tags, and you get this.
 
Chan Ag
Rancher
Posts: 1090
14
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Yes, it is a good practice the way I see it. POISON to me is not a consumable object -- it is a specific object with a very specific purpose ( you do the == check here ). It is something that is a part of the machinery you build and it indicates a clear purpose.
If you are ok with using a poison, I don't see why the consumer shouldn't be put into the queue again, if you have a remote possibility of having to adapt to multiple consumers in the future.

When I was trying to reason it long time back, I thought that a Consumer should only consume the queue elements. It should not produce the queue elements. Hence a consumer should not put the poison back into the queue. But if you are using a poison as a flag, you have already implied that a poison is a poison, not just any other consumable object.

I do not know why Tim Cooke does not recommend it. What I have stated is just my take on it.

Edit : Thanks, Campbell Richie. I shall use it next time.
 
Campbell Ritchie
Marshal
Posts: 55678
161
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
You're welcome

I don't think it was poisons which Tim said he doesn't like.
 
Tim Cooke
Marshal
Posts: 3858
233
Clojure IntelliJ IDE Java
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Indeed. The "poison pill" approach discussed in the thread linked by Chan is the very thing I suggested early on in this thread. I just didn't have a pithy name for it.

My objection was to the unnecessary over engineering for some mythical future requirement that may never materialise. While you might think "I'll do this now just in case we need multiple consumers in the future", this just raises more questions. Take your suggestion to put the poison pill back on the queue:

Future developer wrote:Why is the poison pill going back on the queue? Are there more consumers?
-- No. You're sending future developers on a wild goose chase

Future developer wrote:I only see one consumer, but there must be more otherwise why else put the pill back on the queue?
-- The inconsistency reduces trust in your code.

Future developer wrote:Obviously whoever wrote this has designed it for concurrency, so we can just add more producers and consumers with no other changes
-- An incorrect assumption which will likely lead to unpleasantness at best, a production outage at worst.

Design for the requirements you have today and deliver the simplest thing you can that satisfies those requirements.
 
Chan Ag
Rancher
Posts: 1090
14
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Tim Cooke wrote:

Future developer wrote:Why is the poison pill going back on the queue? Are there more consumers?
-- No. You're sending future developers on a wild goose chase

.... and the other two questions and the reasons mentioned.


A poison in the queue should denote that the single producer of this system ( this producer object, consumer/consumers, this queue ) has terminated, not that there are more consumers. It is only the system manager's ( any controller kind of object ) task to manage the queue and the number of producers and the consumers as affiliates to the system. It is this manager that decides the life of the queue.


 
Tushar Goel
Ranch Hand
Posts: 934
4
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Remember YAGNI..
 
Roy Wood
Greenhorn
Posts: 7
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks for the contributions guys.
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!