Sunday, August 5, 2012

Posix Thread synchronization with Semaphore

In my previous post , we have seen how to synchronize threads with Mutex. In this post we will see how to synchronize threads using semaphore. Also we will undersatand semaphore in terms of POSIX and we'll not discuss about Linux system V semaphore (will discuss in another post).

In simple terms a semaphore can be thought of a variable with two operation: wait and signal. the wait operation decrement the counter where as the signal operation increment the counter.

1. Semaphore functions

int sem_init(sem_t *sem, int pshared, unsigned int value);

It initializes a semaphore pointed to by sem. The pshared param is used to specify the type of semaphore. If it's value is 0, then the semaphore is local to the current process. Otherwise, the semaphore may be shared between processes. The value param is used to give an initial value to semaphore.

int sem_wait(sem_t * sem);

The sem_wait function atomically decreases the value of the semaphore by one. So if you call sem_wait on a semaphore with a value of 2, then the value of semaphore will be decreased to 1. If you call sem_wait on a semaphore with a value of 0, then the thread will be blocked until some other thread has incremented the value to a positive number. If multiple threads are blocked waiting on a semaphore before some other thread increment the semaphore, then only one of them will get to decrement the semaphore and continue. The thread which will get the samaphore is decided by the system and the decesion making technique may vary from system to system i.e. it may be FIFO or may be based on some priority.

int sem_post(sem_t * sem);

The sem_post function atomically increases the value of the semaphore by one. It's the reverse of sem_wait.

It's worth noting that, I have used to term "atomically" with sem_wait and sem_post operation. Atomically means that if two threads simultaneously try to increase or decrease the value of a single semaphore by 1, they do not interfere with each other.

int sem_destroy(sem_t * sem);

It destroys the semaphore pointed to by sem.

2. Code Example:

There are a number of types of semaphore. But here we will use a binary semaphore. A binary semaphore takes only values of 0 or 1 and can be used like a mutex. In fact I have replaced the mutex functions from the example in the previous post , with the semaphore functions in the below example. We'll call it semaphore.c.

semaphore.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>

sem_t smp;
int counter = 0;

void* start_function(void* value)
{
printf("%s is now entering the thread function.\n", (char*)value);
sem_wait(&smp);
counter++;
sleep(2);
sem_post(&smp);
printf("%s is now leaving the thread function.\n", (char*)value);
printf("Value of counter is: %d\n", counter);
pthread_exit(value);
}

main()
{
int res;
pthread_t thread1, thread2;

sem_init(&smp, 0, 1);

res = pthread_create(&thread1, NULL, start_function, "Thread1");
if (res != 0) {
perror("Creation of thread failed");
exit(EXIT_FAILURE);
}

res = pthread_create(&thread2, NULL, start_function, "Thread2");
if (res != 0) {
perror("Creation of thread failed");
exit(EXIT_FAILURE);
}

res = pthread_join(thread1, NULL);
if (res != 0) {
perror("Joining of thread failed");
exit(EXIT_FAILURE);
}

res = pthread_join(thread2, NULL);
if (res != 0) {
perror("Joining of thread failed");
exit(EXIT_FAILURE);
}

sem_destroy(&smp);
}

Now when you compile and run the program, you will see:

gcc -o semaphore semaphore.c -lpthread
./semaphore
Thread1 is now entering the thread function.
Thread2 is now entering the thread function.
Thread1 is now leaving the thread function.
Value of counter is: 1
Thread2 is now leaving the thread function.
Value of counter is: 2


What will happen if we don't use semaphore? Try that out.

No comments:

Post a Comment