Este segundo ejemplo agrega un nivel de complejidad: administramos un spool de impresión que atiende trabajos urgentes antes que los normales utilizando dos colas circulares.
El laboratorio comparte una impresora. Los usuarios pueden enviar trabajos normales o urgentes. El operador quiere:
Implementaremos una estructura reutilizable de cola circular y la instanciaremos dos veces: una para trabajos urgentes y otra para normales.
#define CAP_IMP 20
#define MAX_NOMBRE_DOC 48
typedef struct {
char archivo[MAX_NOMBRE_DOC];
int paginas;
} Trabajo;
typedef struct {
Trabajo datos[CAP_IMP];
int frente;
int final;
int cantidad;
} ColaTrabajos;
void cola_init(ColaTrabajos *cola) {
cola->frente = 0;
cola->final = 0;
cola->cantidad = 0;
}
int cola_encolar(ColaTrabajos *cola, Trabajo t) {
if (cola->cantidad == CAP_IMP) {
return 0;
}
cola->datos[cola->final] = t;
cola->final = (cola->final + 1) % CAP_IMP;
cola->cantidad++;
return 1;
}
int cola_desencolar(ColaTrabajos *cola, Trabajo *t) {
if (cola->cantidad == 0) {
return 0;
}
*t = cola->datos[cola->frente];
cola->frente = (cola->frente + 1) % CAP_IMP;
cola->cantidad--;
return 1;
}
El gestor recibe dos colas y determina la prioridad al procesar.
typedef struct {
ColaTrabajos urgente;
ColaTrabajos normal;
} GestorImpresion;
void gestor_init(GestorImpresion *gestor) {
cola_init(&gestor->urgente);
cola_init(&gestor->normal);
}
int agregarTrabajo(GestorImpresion *gestor, const char *archivo, int paginas, int esUrgente) {
Trabajo t;
strncpy(t.archivo, archivo, MAX_NOMBRE_DOC - 1);
t.archivo[MAX_NOMBRE_DOC - 1] = '\0';
t.paginas = paginas;
return esUrgente ? cola_encolar(&gestor->urgente, t) : cola_encolar(&gestor->normal, t);
}
int procesarTrabajo(GestorImpresion *gestor, Trabajo *t) {
if (cola_desencolar(&gestor->urgente, t)) {
return 1;
}
return cola_desencolar(&gestor->normal, t);
}
void mostrarEstado(const GestorImpresion *gestor) {
printf("Urgentes (%d):\n", gestor->urgente.cantidad);
for (int i = 0, idx = gestor->urgente.frente; i < gestor->urgente.cantidad; ++i, idx = (idx + 1) % CAP_IMP) {
printf(" ! %s (%d pags)\n", gestor->urgente.datos[idx].archivo, gestor->urgente.datos[idx].paginas);
}
printf("Normales (%d):\n", gestor->normal.cantidad);
for (int i = 0, idx = gestor->normal.frente; i < gestor->normal.cantidad; ++i, idx = (idx + 1) % CAP_IMP) {
printf(" - %s (%d pags)\n", gestor->normal.datos[idx].archivo, gestor->normal.datos[idx].paginas);
}
}
Un menú similar al del ejemplo anterior permite agregar trabajos, procesarlos y consultar el estado.
void limpiarBuffer(void) {
int c;
while ((c = getchar()) != '\n' && c != EOF) {}
}
int main(void) {
GestorImpresion gestor;
gestor_init(&gestor);
int opcion;
char archivo[MAX_NOMBRE_DOC];
int paginas;
Trabajo trabajo;
do {
puts("\n1) Agregar trabajo normal");
puts("2) Agregar trabajo urgente");
puts("3) Procesar siguiente");
puts("4) Mostrar estado");
puts("0) Salir");
printf("Opcion: ");
if (scanf("%d", &opcion) != 1) {
puts("Entrada invalida");
limpiarBuffer();
continue;
}
limpiarBuffer();
switch (opcion) {
case 1:
case 2:
printf("Archivo: ");
if (!fgets(archivo, sizeof archivo, stdin)) break;
archivo[strcspn(archivo, "\n")] = '\0';
printf("Paginas: ");
if (scanf("%d", &paginas) != 1) {
puts("Valor incorrecto");
limpiarBuffer();
break;
}
limpiarBuffer();
if (agregarTrabajo(&gestor, archivo, paginas, opcion == 2)) {
puts("Trabajo encolado correctamente.");
} else {
puts("Cola correspondiente llena.");
}
break;
case 3:
if (procesarTrabajo(&gestor, &trabajo)) {
printf("Imprimiendo: %s (%d pags)\n", trabajo.archivo, trabajo.paginas);
} else {
puts("No hay trabajos pendientes.");
}
break;
case 4:
mostrarEstado(&gestor);
break;
case 0:
puts("Fin del gestor");
break;
default:
puts("Opcion no valida");
}
} while (opcion != 0);
return 0;
}
Copia este archivo único para probar el gestor de impresión sin pegar fragmentos por separado:
#include <stdio.h>
#include <string.h>
#define CAP_IMP 20
#define MAX_NOMBRE_DOC 48
typedef struct {
char archivo[MAX_NOMBRE_DOC];
int paginas;
} Trabajo;
typedef struct {
Trabajo datos[CAP_IMP];
int frente;
int final;
int cantidad;
} ColaTrabajos;
typedef struct {
ColaTrabajos urgente;
ColaTrabajos normal;
} GestorImpresion;
void cola_init(ColaTrabajos *cola) {
cola->frente = 0;
cola->final = 0;
cola->cantidad = 0;
}
int cola_encolar(ColaTrabajos *cola, Trabajo t) {
if (cola->cantidad == CAP_IMP) {
return 0;
}
cola->datos[cola->final] = t;
cola->final = (cola->final + 1) % CAP_IMP;
cola->cantidad++;
return 1;
}
int cola_desencolar(ColaTrabajos *cola, Trabajo *t) {
if (cola->cantidad == 0) {
return 0;
}
*t = cola->datos[cola->frente];
cola->frente = (cola->frente + 1) % CAP_IMP;
cola->cantidad--;
return 1;
}
void gestor_init(GestorImpresion *gestor) {
cola_init(&gestor->urgente);
cola_init(&gestor->normal);
}
int agregarTrabajo(GestorImpresion *gestor, const char *archivo, int paginas, int esUrgente) {
Trabajo t;
strncpy(t.archivo, archivo, MAX_NOMBRE_DOC - 1);
t.archivo[MAX_NOMBRE_DOC - 1] = '\0';
t.paginas = paginas;
return esUrgente ? cola_encolar(&gestor->urgente, t) : cola_encolar(&gestor->normal, t);
}
int procesarTrabajo(GestorImpresion *gestor, Trabajo *t) {
if (cola_desencolar(&gestor->urgente, t)) {
return 1;
}
return cola_desencolar(&gestor->normal, t);
}
void mostrarEstado(const GestorImpresion *gestor) {
printf("Urgentes (%d):\n", gestor->urgente.cantidad);
for (int i = 0, idx = gestor->urgente.frente; i < gestor->urgente.cantidad; ++i, idx = (idx + 1) % CAP_IMP) {
printf(" ! %s (%d pags)\n", gestor->urgente.datos[idx].archivo, gestor->urgente.datos[idx].paginas);
}
printf("Normales (%d):\n", gestor->normal.cantidad);
for (int i = 0, idx = gestor->normal.frente; i < gestor->normal.cantidad; ++i, idx = (idx + 1) % CAP_IMP) {
printf(" - %s (%d pags)\n", gestor->normal.datos[idx].archivo, gestor->normal.datos[idx].paginas);
}
}
void limpiarBuffer(void) {
int c;
while ((c = getchar()) != '\n' && c != EOF) {}
}
int main(void) {
GestorImpresion gestor;
gestor_init(&gestor);
int opcion;
char archivo[MAX_NOMBRE_DOC];
int paginas;
Trabajo trabajo;
do {
puts("\n1) Agregar trabajo normal");
puts("2) Agregar trabajo urgente");
puts("3) Procesar siguiente");
puts("4) Mostrar estado");
puts("0) Salir");
printf("Opcion: ");
if (scanf("%d", &opcion) != 1) {
puts("Entrada invalida");
limpiarBuffer();
continue;
}
limpiarBuffer();
switch (opcion) {
case 1:
case 2:
printf("Archivo: ");
if (!fgets(archivo, sizeof archivo, stdin)) break;
archivo[strcspn(archivo, "\n")] = '\0';
printf("Paginas: ");
if (scanf("%d", &paginas) != 1) {
puts("Valor incorrecto");
limpiarBuffer();
break;
}
limpiarBuffer();
if (agregarTrabajo(&gestor, archivo, paginas, opcion == 2)) {
puts("Trabajo encolado correctamente.");
} else {
puts("Cola correspondiente llena.");
}
break;
case 3:
if (procesarTrabajo(&gestor, &trabajo)) {
printf("Imprimiendo: %s (%d pags)\n", trabajo.archivo, trabajo.paginas);
} else {
puts("No hay trabajos pendientes.");
}
break;
case 4:
mostrarEstado(&gestor);
break;
case 0:
puts("Fin del gestor");
break;
default:
puts("Opcion no valida");
}
} while (opcion != 0);
return 0;
}
El tercer ejemplo práctico incorporará hilos y almacenamiento persistente para acercarse a un spool profesional.