La mejor manera de entender la diferencia es con la ayuda de un ejemplo. A continuación se muestra el programa para resolver el problema del consumidor clásico productor mediante semáforo. Para proporcionar exclusión mutua, generalmente utilizamos un semáforo binario o mutex y para proporcionar sincronización usar contando semáforo
BufferSize = 3;
semaphore mutex = 1; // used for mutual exclusion
semaphore empty = BufferSize; // used for synchronization
semaphore full = 0; // used for synchronization
Producer()
{
int widget;
while (TRUE) { // loop forever
make_new(widget); // create a new widget to put in the buffer
down(&empty); // decrement the empty semaphore
down(&mutex); // enter critical section
put_item(widget); // put widget in buffer
up(&mutex); // leave critical section
up(&full); // increment the full semaphore
}
}
Consumer()
{
int widget;
while (TRUE) { // loop forever
down(&full); // decrement the full semaphore
down(&mutex); // enter critical section
remove_item(widget); // take a widget from the buffer
up(&mutex); // leave critical section
consume_item(widget); // consume the item
}
}
En el código anterior la variable mutex proporciona la exclusión mutua (permitir sólo un hilo para acceder a la sección crítica), mientras que completa y la variable de vacío se utilizan para synchonization (a aribtrate el acceso de recurso compartido entre varios hilo).