Reputation: 6372
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
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
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