7.5 Sharing a Resource
In the previous queueing situations, at each station there was one resource that was used during the service operation. It is often the case that a resource can be shared across multiple activities. For example, suppose a worker is assigned to attend two machining processes. The resource is said to be shared.
Figure 7.7 illustrates this notion of sharing a resource between two activities. Notice that in the figure, there are two queues, one each for the two different types of parts. If there had been only a single queue to hold both parts, the modeling is very similar to the single queue station modeling that has already been presented. With only one queue, we would need to use a queue discipline to order the parts in the queue and to schedule the end of service according to the appropriate service time distribution for the given part. In the case of two queues, we need to determine how the resource will select the queue in addition to the queue discipline. For the sake of simplicity, let us assume that the queue discipline is first in first out (FIFO). Then, in the case of two queues, we need a mechanism or rule to determine which queue will be processed first, when the resource becomes available.
How many events are there for the situation in Figure 7.7? From the strict definition of an event, there are six events in this situation. There are the two arrival events for each type of part. There are two different begin service events and two different end service events. From an implementation perspective, we could model this situation with only two events. We can have a single event that handles both types of arrivals and a single end service event that handles the departure of either type of part. The reason we only need one end service event is because the begin service event logic can be incorporated in the end of service event because whenever a service completes a waiting part will start into service. How many events should you use in your modeling? You should implement the events in a way that facilitates your perspective and your modeling. The trade-off is that when the event logic is consolidated, the logic may be more complicated.
The following listing presents the constructor for the shared queue
example. Notice that two instances of the Queue
and EventGenerator
classes are used. The two event generators are used to implement the
arrival processes for the two types of parts. The two queues are used to
hold the two types of parts. An instance of SResource
is used to
represent the workers that are shared between the two activities. The
activity times are represented by two RandomVariable
instances. A
TimeWeighted
instance and a ResponseVariable
instance are used to
collect statistics on the number of parts in the system and the time
spent in the system, respectively.
public class SharedResource extends SchedulingElement {
private final EventGenerator myTypeAGenerator;
private final EventGenerator myTypeBGenerator;
private final TimeWeighted myNumInSystem;
private final ResponseVariable mySystemTime;
private final SResource myServers;
private final Queue<QObject> myTypeAWaitingQ;
private final Queue<QObject> myTypeBWaitingQ;
private final EndServiceEventAction myEndServiceEventAction;
private final RandomVariable myServiceRVTypeA;
private final RandomVariable myServiceRVTypeB;
public SharedResource(ModelElement parent, int numServers, RVariableIfc tbaA,
RVariableIfc tbaB, RVariableIfc stA, RVariableIfc stB, String name) {
super(parent, name);
myTypeAGenerator = new EventGenerator(this, new TypeAArrivals(), tbaA, tbaA);
myTypeBGenerator = new EventGenerator(this, new TypeBArrivals(), tbaB, tbaB);
myServiceRVTypeA = new RandomVariable(this, stA, "Service RV A");
myServiceRVTypeB = new RandomVariable(this, stB, "Service RV B");
myServers = new SResource(this, numServers, "Servers");
myTypeAWaitingQ = new Queue<>(this, getName() + "_QA");
myTypeBWaitingQ = new Queue<>(this, getName() + "_QB");
mySystemTime = new ResponseVariable(this, "System Time");
myNumInSystem = new TimeWeighted(this, "Num in System");
myEndServiceEventAction = new EndServiceEventAction();
}
The following listing presents the arrivals of the parts via the
generator listeners and the beginning of service. In line 5, the number
of parts is incremented. Then, in line 6, the arriving part is
represented with a new QObject
, which is immediately enqueued. The if
statement starting at line 7, checks if the resource has available units
and if so, begins serving the next part. In the serveNext()
method, the
two queues are checked. Notice that the queue for type A parts is
checked first. If the queue is not empty, then the part is removed and
started into service using the service time distribution for type A
parts. If there are no type A parts, the queue for type B parts is
checked in a similar fashion. Because the type A queue is checked first,
it will have priority over the type B part queue.
private class TypeAArrivals implements EventGeneratorActionIfc {
@Override
public void generate(EventGenerator generator, JSLEvent event) {
myNumInSystem.increment();
myTypeAWaitingQ.enqueue(new QObject(getTime()));
if (myServers.hasAvailableUnits()) { // server available
serveNext();
}
}
}
private class TypeBArrivals implements EventGeneratorActionIfc {
@Override
public void generate(EventGenerator generator, JSLEvent event) {
myNumInSystem.increment();
myTypeBWaitingQ.enqueue(new QObject(getTime()));
if (myServers.hasAvailableUnits()) { // server available
serveNext();
}
}
}
private void serveNext() {
//logic to choose next from queues
// if both have waiting parts, assume part type A has priority
if (myTypeAWaitingQ.isNotEmpty()) {
QObject partA = myTypeAWaitingQ.removeNext(); //remove the next customer
myServers.seize();
// schedule end of service
scheduleEvent(myEndServiceEventAction, myServiceRVTypeA, partA);
} else if (myTypeBWaitingQ.isNotEmpty()) {
QObject partB = myTypeBWaitingQ.removeNext(); //remove the next customer
myServers.seize();
// schedule end of service
scheduleEvent(myEndServiceEventAction, myServiceRVTypeB, partB);
}
}
The following code listing presents the end of service event logic for the
shared resource example. The end of service logic is very similar to
logic that we have seen for the departure from a queueing station. In
the end of service action, the part that is leaving is passed to the
outer class for statistical collection in the departingSystem()
method.
The resource is released and the queues are checked to see if a part is
waiting. Note that both queues are checked, such that if either queue
has a waiting part, the logic for serving the next part is invoked.
private boolean checkQueues() {
return (myTypeAWaitingQ.isNotEmpty() || myTypeBWaitingQ.isNotEmpty());
}
private class EndServiceEventAction implements EventActionIfc<QObject> {
@Override
public void action(JSLEvent<QObject> event) {
QObject leavingPart = event.getMessage();
myServers.release();
if (checkQueues()) { // queue is not empty
serveNext();
}
departSystem(leavingPart);
}
}
private void departSystem(QObject leavingPart) {
mySystemTime.setValue(getTime() - leavingPart.getCreateTime());
myNumInSystem.decrement(); // part left system
}
Finally, the following code shows how to setup and run the example. The system is configured with the time between arrivals for type A parts and type B parts having exponential distributions with means of 4 and 6 time units, respectively. The service time distributions are also exponential with means of 3 and 5, respectively for type A and B parts. The capacity of the resource is set at 2 workers.
public static void main(String[] args) {
Simulation sim = new Simulation("Shared Resource Example");
// get the model
Model m = sim.getModel();
// add to the main model
RVariableIfc tbaA = new ExponentialRV(4.0);
RVariableIfc tbaB = new ExponentialRV(6.0);
RVariableIfc stA = new ExponentialRV(3.0);
RVariableIfc stB = new ExponentialRV(5.0);
SharedResource sr = new SharedResource(m, 2, tbaA, tbaB, stA, stB, "SR");
// set the parameters of the experiment
sim.setNumberOfReplications(30);
sim.setLengthOfReplication(20000.0);
sim.setLengthOfWarmUp(5000.0);
SimulationReporter r = sim.makeSimulationReporter();
System.out.println("Simulation started.");
sim.run();
System.out.println("Simulation completed.");
r.printAcrossReplicationSummaryStatistics();
}
The results indicate that the type B parts wait significantly longer on average than the type A parts. It should be apparent that with some additional design that the shared resource example could be generalized into a class that could be used in other simulation models similar to how the single station queue has been generalized. This is one of the important advantages of an object-oriented approach to simulation modeling.
|