Why are semaphores used?
While using threads, we encounter several conditional issues involving race conditions. This occurs when two or more threads need the same data or information at the same time that causing conflict. So, to avoid this type of conflicting situation, we use semaphores. There are three main types of semaphores. One is a binary semaphore, and another one is a counting semaphore.
We use different functions in the range of semaphore like sem_wait, sem_post, and sem_init. Sem_init is the topic under consideration further in this article.
As we discussed above, to initialize the semaphore in threads, we use the sem_init function. Here we use a flag or a banner that identifies the sharing of semaphore with fork() procedure.
Sem: This feature assists the semaphore to be in a ready state.
Pshared: This parameter argument is fundamental in the declaration of semaphore. As it determines the status of the newly initialized semaphore. Whether or not it should be shared between the processes or threads. If the value is non-zero, it means that the semaphore is shared between two or more processes, and if the value is zero, then it means the semaphore is shared between the threads.
Value: It specifies the value that is to be assigned to the newly created semaphore that is assigned initially.
Implementation of sem_init
To execute semaphores in the C program, we need a GCC compiler. But this is not sufficient. “–lpthread” is used to execute the code. ‘a.c’ is the file name. Another thing is that here we use ‘.out’ with the file name instead of using the file independently.
First, we add two libraries having semaphores and pthread to indulge the usage of c packages. Like sem_init other semaphores are used in this program; here, we will discuss them.
This function is used to hold a semaphore or to keep waiting. If the value provided to the semaphore is negative, the calling is blocked, and the cycle is closed. Whereas any other thread, when called, the blocked semaphores are awakened.
Sem_post method is used to increase the semaphore value. The value is incremented by sem_post when it is called.
If we want to destroy semaphore, we use the sem_destroy method. Now again, focus on the source code provided here. First, the “await” function is used here. It will make the thread wait first so that others can perform a task. A message is displayed that the thread is entered on calling the function. After that, a “sleep” function is called for 5 seconds.
Two threads are created according to the main functions, 2 threads are created, but the first one sleeps for 5 seconds after the lock is acquired. So the second thread is not entered when it is called. It will enter after 5-2 seconds when it is called.
Sem_post will work after the sleep function; sem_post will work and show a complete status message. In the main program, the semaphore is initialized first, and then both threads are created using pthread. We use the pthread_join function to join the threads. And at the end, semaphores are destroyed.
Save the file with the extension of .c; code will be compiled, and execution will be done. On execution, you will see that the first message is displayed, and then it takes a few seconds to complete, as we have provided the sleep function with 5 seconds, so after that time, the second message for the first thread is displayed.
Frequently the first message for the second thread is displayed.
The second message will again take time to proceed.
Before moving towards the second example, first, we need to understand the concept of the reader’s writer’s problem. Suppose that a database you want to share between the processes runs concurrently. Some of these processes or threads may read the database only. At the same time, others may like to modify the database. We discriminate between these two by declaring the first one as a reader and the second one as a writer. If two readers access the shared data, it will cause no effect.
To minimize the occurrence of these sorts of difficulties, we need to assist writers in accessing the shared database to write in it. This problem is synchronized and known as the readers-writers problem.
There are many variations in this problem. The first one deals with the issue that no reader will be waiting unless a writer uses shared objects.
This program provides the solution for the first reader-writer problem. In this C source code, we used 10 readers and 5 procedures to demonstrate the solution. The first two counters are taken that are referred to as zero. The nonreader identifies the number of the reader. Moving towards the writer function, two semaphore functions are used here, the first one is the wait, and the latter is the post. This will display the writer’s number.
After the writer function, the reader function is declared here. The writer will modify the database so the reader cannot enter or change anything acquired by a lock.
The nonreader count is then incremented. Here a check is applied of if-statement. If the value is 1, it means it is the first reader so that the writer will be blocked. If the nonreader is 0, after checking, it means it is the last reader, so we will now allow the writer for the modification.
We will move towards the main program after both the reader and writer function. Here we have initialized 10 readers and 5 writers. The sem_init function will initialize the semaphore. For loops are used here separately for both the readers and writers. Pthread_create will create the read and write functions. Furthermore, pthread_join will join the threads. Each for loop will use this joint 5 times for writer purpose and then 10 times for the reader purpose.
And at the end, the semaphore is destroyed respectively after usage. Compile the code and then execute it. You will see that random numbers for the reader are generated within 10 array sizes with count 1. And for the writer, 5 numbers are modified.
The article ‘sem_init’ is a function used by the semaphores in the multithreading process to prioritize the tasks occurring concurrently. There are many other functions related to semaphores, also discussed here. We have explained two elementary examples to elaborate on the usage of sem_init in the functions and other features.