In this section we look at some examples of how more complex scenarios involving queues can be modeled.
This behavior occurs when a member of the queue leaves in preference for an alternative queue. We can model the swapping of an event e between the queues q1 and q2 as follows :
if (condition) { if (q1 -> extract(e)) // from every position q2 -> append(e); // append to second queue }where condition could be something as the checking of the sizes of the two queues.
If a queue is too long it usually deters further additions. This phenomenon is called balking and its implementation is straightforward :
if (q -> size() < MAX) q -> append(e); // append if possible
The event is appended when there is room for it.
Reneging is the premature departure of a customer from a queue before it has had any service. This is typically the case when its waiting time exceeded some upper limit. We could model this by checking the entire queue with the queue::cancel method, but more efficient is the following addition to the arrival event :
cancel* cn = new cancel(this); // give customer to the cancel event sim -> schedule(cn,MAX); // schedule cancel eventand the extracting of the event in the cancel::operator function. The queue::extract function does nothing if the event is not in the queue.
int cancel::operator()() { if (q -> extract(e)) // extract and terminate e if in the queue sim -> terminate(e); return OK; }
Pre-emption occurs when a customer being served is interrupted by the server in favor of a new customer that has a higher priority. The interrupted customer can then wait to complete its service, wait to be served again or simply leave the system. The interrupting arrival then takes the place of the interrupted event. Consider the following behavior of the server entity of the M/M/1 queue. Events should then receive a queuing priority when created and the event currently being served should be set to NULL when terminated.
int server::operator()() { if (!(q -> empty())) // if an event waiting if (!current) // if nothing being served return serve(); // just serve else if (q -> front() -> queuingpriority() > current -> queuingpriority()) return preempt(); // front has a higher priority } int server::serve() { customer* c = (customer* )q -> removefront(); r -> acquire(); current = c; // record as current sim -> schedule(c,g -> exponential(meanservice)); c -> phase(DEPARTURE); return OK; } int server::preempt() { sim -> passivate(current); // extract departure r -> release(); // and depart sim -> terminate(current); customer* c = (customer* )q -> removefront(); r -> acquire(); current = c; // record as current sim -> schedule(c,g -> exponential(meanservice)); c -> phase(DEPARTURE); return OK; }
If the front can be served we look if there is currently a customer being served (the current variable is set to NULL in the departure event). If so the front is served only if its queuing priority is higher. The front is then recorded as the current.
The arrival and service rates have been considered constant so far. This is not always realistic. The arrival rate at a supermarket shows a noticeable peak around evening for example. We can easily model this by constructing an arrivaltime function as follows :
double arrivaltime() { double t = g -> exponential(MEAN1); if (sim -> clock() + t < TIME) // use the appropriate mean return t; // based on the current else // simulation time return (g -> exponential(MEAN2)); }
and we have different arrival rates at different times, if this function is used when a new arrival is scheduled.