Jack Twain
Jack Twain

Reputation: 6372

How to start, pause and stop producer/consumer relationship?

I'm definitely in a concurrency hell. I'm not able to find a good/efficient solution to what I'm trying to do. I have a Producer thread that is reading text file and puts the information in a shared BlockedQueue. And I have a Consumer that uses the shared BlockedQueue to read the data and does heavy stuff with the data. I have a GUI with three buttons: Start, Pause and Stop.

The Producer and Consumer both implement Runnable and provide methods to access information about the computation of each one (e.g. return some statistics or some Objects)

With the Start option I want the Producer to open a file and start putting data into the BlockedQueue. And also the Consumer to start taking the data and do its computation.

With the Pause option I want the Producer to halt putting data into the BlockedQueue but at the same time I want be able to access the instance variables of the Producer. The same is for the Consumer, I want to halt doing the heavy stuff but still be able to access some instance variables and methods defined in the Consumer.

With the Stop option I want the Producer and Consumer to reset ... i.e. as if to Start from clean.

My question is how to efficiently implement this? Especially to check for pause?

Would something like this psudo-code be efficient?

Enum state;

class Producer implements Runnable {
    public List someList;//accessed from the event-dispatching thread 
    public void Run() {
        synchronized(state) {
            if(state == Enum.paused) {
                //do nothing
            }
            else if(state == Enum.running) {
                //put stuff into BlockedQueue
            }
            else if (state == Enum.stopped) {
               // reopen file and set state = running
            }


        }
    }
}

class Consumer implements Runnable {
    public Map someMap;//accessed from the event-dispatching thread 
    public void Run() {
        synchronized(state) {
            if(state == Enum.paused) {
                //do nothing
            }
            else if(state == Enum.running) {
                //start consuming from the BlockedQueue and do heavy computation
            }
            else if (state == Enum.stopped) {
               // clear stuff to start clean and set state = running
            }


        }
    }
}

Upvotes: 0

Views: 652

Answers (2)

Gray
Gray

Reputation: 116888

My question is how to efficiently implement this? Especially to check for pause? Would something like this psudo-code be efficient?

I think using a field on the Consumer and Producer is a fine way to do this. You need to make sure the field is volatile so that any updates to the enum are properly synchronized between the threads. Or if you use synchronized keyword, you need to have a synchronized when it is updated. volatile is better in this case since there is no reason to block.

 public class Consumer {
    private volatile Enum state;
    ...
    if(state == Enum.paused) {

People have mentioned a "poison pill" as a solution. It's when you put an object into the queue which changes the state. The problem with this solution is that if the consumer is working on the object, it won't be checking the queue so this won't work. I guess you could peek() at the queue but the state field should work fine.

The Producer and Consumer both implement Runnable and provide methods to access information about the computation of each one (e.g. return some statistics or some Objects)

The Producer and Consumer objects will still exist and will be accessible. In all cases, you need to make sure that any updates made to fields to be shared between threads need to be properly synchronized.

My question is how to efficiently implement this? Especially to check for pause?

In terms of efficiency, a volatile field access is seem to be ~100 times slower than a typical field access. I wouldn't put a state check on every line but if you check it at the top of each processing loop or do something like if (loopCounter % 1000 == 0) to check for pause every X times through the loop, you shouldn't see much if any performance hit.

Upvotes: 1

Val
Val

Reputation: 11107

You may tag the messages entering the queue whether they are data or control messages. Every thread has only one input (message) queue.

Upvotes: 0

Related Questions