[Linux operating system] Semaphore implements producer consumer model

When it comes to multi-threaded programming, the producer consumer problem is often encountered. In Linux system programming, we can use semaphores to implement the producer consumer model to ensure synchronization and mutual exclusion between threads.

insert image description here

What is the producer consumer problem?

The producer consumer problem is a classic multithreaded programming problem that involves two types of threads: producers and consumers. Producer threads generate data and put it into a shared buffer, while consumer threads fetch data from the shared buffer and process it. Synchronization between producers and consumers is required to ensure that the producer does not continue to produce data when the buffer is full, and the consumer does not continue to consume data when the buffer is empty.

Using semaphores to implement the producer consumer model

In Linux system programming, we can use semaphores to implement the producer consumer model. A semaphore is a mechanism used to implement synchronization and mutual exclusion in multithreaded programming. It can be used to solve classic problems in multi-threaded programming such as the producer-consumer problem, the reader-writer problem, and the philosopher's dining problem.

The principle of semaphore

A semaphore is a counter used to control access to shared resources. When a thread needs to access a shared resource, it first checks the value of the semaphore. If the value of the semaphore is greater than 0, it means that resources are available and the thread can continue execution. If the value of the semaphore is 0, it means that there are no resources available and the thread needs to wait.

When a thread finishes accessing a shared resource, it will decrement the value of the semaphore by 1, indicating that a resource is used. When a thread releases a shared resource, it will increase the value of the semaphore by 1, indicating that a resource is released. Other threads can check the value of the semaphore to determine whether they can continue to execute.

semaphore function

In Linux system programming, we can use functions such sem_init()as , sem_wait(), sem_post()and sem_destroy()to operate semaphores.

  • int sem_init(sem_t *sem, int pshared, unsigned int value): Initialize the semaphore. semIt is a pointer to a semaphore, psharedspecifies the type of the semaphore, and valuespecifies the initial value of the semaphore. Returns 0 if successful; otherwise, returns -1.

  • int sem_wait(sem_t *sem): Wait for a semaphore. If the value of the semaphore is greater than 0, decrement the value of the semaphore by 1 and continue execution; if the value of the semaphore is 0, the thread needs to wait. Returns 0 if successful; otherwise, returns -1.

  • int sem_post(sem_t *sem): Release the semaphore. Add 1 to the value of the semaphore, indicating that a resource is released. Returns 0 if successful; otherwise, returns -1.

  • int sem_destroy(sem_t *sem): Destroy the semaphore. Returns 0 if successful; otherwise, returns -1.

Example code explained

The following is a sample code that implements the producer-consumer model using semaphores:

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

#define BUFFER_SIZE 10

int buffer[BUFFER_SIZE];
sem_t empty;
sem_t full;
pthread_mutex_t mutex;

void *producer(void *arg) {
    
    
    int item;
    while (1) {
    
    
        item = rand() % 100;  // 生成随机数据
        sem_wait(&empty);  // 等待空闲空间
        pthread_mutex_lock(&mutex);  // 加锁
        // 将数据放入缓冲区
        // ...
        pthread_mutex_unlock(&mutex);  // 解锁
        sem_post(&full);  // 通知有数据可用
    }
}

void *consumer(void *arg) {
    
    
    int item;
    while (1) {
    
    
        sem_wait(&full);  // 等待有数据可用
        pthread_mutex_lock(&mutex);  // 加锁
        // 从缓冲区取出数据并进行处理
        // ...
        pthread_mutex_unlock(&mutex);  // 解锁
        sem_post(&empty);  // 通知有空闲空间
    }
}

int main() {
    
    
    pthread_t producerThread, consumerThread;

    // 初始化信号量和互斥锁
    sem_init(&empty, 0, BUFFER_SIZE);
    sem_init(&full, 0, 0);
    pthread_mutex_init(&mutex, NULL);

    // 创建生产者线程和消费者线程
    pthread_create(&producerThread, NULL, producer, NULL);
    pthread_create(&consumerThread, NULL, consumer, NULL);

    // 等待线程结束
    pthread_join(producerThread, NULL);
    pthread_join(consumerThread, NULL);

    // 销毁信号量和互斥锁
    sem_destroy(&empty);
    sem_destroy(&full);
    pthread_mutex_destroy(&mutex);

    return 0;
}

In the above code, we have used a BUFFER_SIZEbuffer of size to simulate a shared resource. emptyand fullare two semaphores used to control the amount of free space and stored data. mutexIs a mutex that protects access to shared resources.

The producer thread generates random data and puts the data into the buffer. Before putting data, it first waits for a semaphore of free space empty, and continues execution if there is free space. After putting in the data, it adds 1 to the semaphore of the stored data full.

The consumer thread takes data from the buffer and processes it. Before fetching data, it first waits for a semaphore that has stored data full, and continues execution if data is available. After fetching the data, it increments the semaphore of free space emptyby 1.

in conclusion

Semaphore is a synchronization tool for multi-threaded programming, which can be used to solve the synchronization and mutual exclusion problems in the producer-consumer model. By using semaphores, we can control the execution order of threads and ensure mutual exclusion and synchronization between threads.

In Linux system programming, the use of semaphores needs to include header files <semaphore.h>, and operate the semaphores through the following functions:

  • int sem_init(sem_t *sem, int pshared, unsigned int value): Initialize the semaphore. semIt is a pointer to the semaphore, psharedspecifies the sharing method of the semaphore, and valueis the initial value of the semaphore. Returns 0 if successful; otherwise, returns -1.

  • int sem_wait(sem_t *sem): Wait for a semaphore. If the value of the semaphore is greater than 0, the value of the semaphore is decremented by 1, indicating that a resource is occupied. If the value of the semaphore is 0, the thread needs to wait. Returns 0 if successful; otherwise, returns -1.

  • int sem_post(sem_t *sem): Release the semaphore. Add 1 to the value of the semaphore, indicating that a resource is released. Returns 0 if successful; otherwise, returns -1.

  • int sem_destroy(sem_t *sem): Destroy the semaphore. Returns 0 if successful; otherwise, returns -1.

By using semaphores and mutexes, we can implement the producer-consumer model and ensure mutual exclusion and synchronization between producers and consumers.

Guess you like

Origin blog.csdn.net/Goforyouqp/article/details/132413152