MST

星途 面试题库

面试题:并发与同步:条件变量与信号量基础使用场景对比

请简要阐述条件变量和信号量在操作系统并发与同步机制中的基础使用场景,并举例说明在生产者 - 消费者模型中,它们分别是如何应用来保证数据一致性和线程协作的。
31.8万 热度难度
操作系统并发与同步

知识考点

AI 面试

面试题答案

一键面试

条件变量的基础使用场景

条件变量通常用于线程间的同步,当某个条件满足时,一个或多个线程会被唤醒。它依赖于一个互斥锁,一般用于等待某个特定条件发生的场景。例如,在一个线程池任务处理场景中,工作线程等待任务队列中有新任务(条件),当有新任务添加到队列时(条件满足),等待的工作线程被唤醒去处理任务。

信号量的基础使用场景

信号量用于控制对共享资源的访问数量。它通过一个计数器来管理,当计数器大于0时,线程可以获取信号量(计数器减1)来访问资源,访问完后释放信号量(计数器加1)。常用于控制对有限资源的并发访问。比如,在数据库连接池场景中,信号量可以用来控制同时获取连接的线程数量,防止过多线程同时获取连接导致资源耗尽。

在生产者 - 消费者模型中条件变量的应用

  1. 数据结构准备:假设有一个共享缓冲区用于生产者和消费者之间的数据传递,同时有一个互斥锁 mutex 用于保护对缓冲区的访问,一个条件变量 cond 用于通知消费者缓冲区有数据。
#include <pthread.h>
#include <stdio.h>

#define BUFFER_SIZE 5
int buffer[BUFFER_SIZE];
int in = 0;
int out = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
  1. 生产者函数:生产者在向缓冲区写入数据前先获取互斥锁,当缓冲区满时等待条件变量 cond,有空间时写入数据并通知消费者。
void *producer(void *arg) {
    int item = 1;
    while (1) {
        pthread_mutex_lock(&mutex);
        while ((in + 1) % BUFFER_SIZE == out) {
            pthread_cond_wait(&cond, &mutex);
        }
        buffer[in] = item++;
        printf("Produced: %d\n", buffer[in]);
        in = (in + 1) % BUFFER_SIZE;
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}
  1. 消费者函数:消费者在从缓冲区读取数据前获取互斥锁,当缓冲区空时等待条件变量 cond,有数据时读取数据并通知生产者。
void *consumer(void *arg) {
    while (1) {
        pthread_mutex_lock(&mutex);
        while (in == out) {
            pthread_cond_wait(&cond, &mutex);
        }
        int item = buffer[out];
        printf("Consumed: %d\n", item);
        out = (out + 1) % BUFFER_SIZE;
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

在生产者 - 消费者模型中信号量的应用

  1. 数据结构准备:同样有共享缓冲区,用两个信号量 empty 表示缓冲区的空闲槽位数量,full 表示缓冲区已占用槽位数量,还有一个互斥锁 mutex 保护对缓冲区的访问。
#include <semaphore.h>
#include <pthread.h>
#include <stdio.h>

#define BUFFER_SIZE 5
int buffer[BUFFER_SIZE];
int in = 0;
int out = 0;
sem_t empty;
sem_t full;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
  1. 生产者函数:生产者先获取 empty 信号量表示有空闲槽位,获取互斥锁后写入数据,然后释放 full 信号量表示有数据可供消费。
void *producer(void *arg) {
    int item = 1;
    while (1) {
        sem_wait(&empty);
        pthread_mutex_lock(&mutex);
        buffer[in] = item++;
        printf("Produced: %d\n", buffer[in]);
        in = (in + 1) % BUFFER_SIZE;
        pthread_mutex_unlock(&mutex);
        sem_post(&full);
    }
    return NULL;
}
  1. 消费者函数:消费者先获取 full 信号量表示有数据,获取互斥锁后读取数据,然后释放 empty 信号量表示有空闲槽位。
void *consumer(void *arg) {
    while (1) {
        sem_wait(&full);
        pthread_mutex_lock(&mutex);
        int item = buffer[out];
        printf("Consumed: %d\n", item);
        out = (out + 1) % BUFFER_SIZE;
        pthread_mutex_unlock(&mutex);
        sem_post(&empty);
    }
    return NULL;
}

通过上述方式,条件变量和信号量在生产者 - 消费者模型中分别通过不同机制保证了数据一致性和线程协作。