MST

星途 面试题库

面试题:C语言多线程的条件变量应用

假设有一个生产者 - 消费者模型,在Linux C语言多线程编程环境下实现。生产者线程生产数据并放入共享缓冲区,消费者线程从共享缓冲区取出数据进行处理。使用条件变量来协调生产者和消费者线程的工作,防止缓冲区空读或满写。请阐述条件变量的原理,并给出关键代码实现以及如何避免条件变量使用过程中的常见错误。
32.5万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

条件变量的原理

条件变量是一种同步机制,用于线程间的协调。它通常与互斥锁配合使用。

  1. 基本概念
    • 条件变量允许线程等待某个条件为真。例如,在生产者 - 消费者模型中,消费者线程可以等待共享缓冲区不为空的条件,生产者线程可以等待共享缓冲区不满的条件。
    • 当一个线程发现条件不满足时,它可以通过条件变量进入睡眠状态,并自动释放相关的互斥锁(以避免死锁)。
    • 当另一个线程改变了条件(例如生产者向共享缓冲区放入数据使缓冲区不为空),它可以唤醒等待在条件变量上的线程。被唤醒的线程会重新获取互斥锁,然后检查条件是否满足。

关键代码实现

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

#define BUFFER_SIZE 5
int buffer[BUFFER_SIZE];
int in = 0;
int out = 0;
int count = 0;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t not_full = PTHREAD_COND_INITIALIZER;
pthread_cond_t not_empty = PTHREAD_COND_INITIALIZER;

// 生产者线程函数
void* producer(void* arg) {
    int item = 1;
    while (1) {
        pthread_mutex_lock(&mutex);
        while (count == BUFFER_SIZE) {
            pthread_cond_wait(&not_full, &mutex);
        }
        buffer[in] = item;
        printf("Produced: %d\n", item);
        in = (in + 1) % BUFFER_SIZE;
        count++;
        pthread_cond_signal(&not_empty);
        pthread_mutex_unlock(&mutex);
        item++;
    }
    return NULL;
}

// 消费者线程函数
void* consumer(void* arg) {
    while (1) {
        pthread_mutex_lock(&mutex);
        while (count == 0) {
            pthread_cond_wait(&not_empty, &mutex);
        }
        int item = buffer[out];
        printf("Consumed: %d\n", item);
        out = (out + 1) % BUFFER_SIZE;
        count--;
        pthread_cond_signal(&not_full);
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

int main() {
    pthread_t producer_thread, consumer_thread;
    pthread_create(&producer_thread, NULL, producer, NULL);
    pthread_create(&consumer_thread, NULL, consumer, NULL);

    pthread_join(producer_thread, NULL);
    pthread_join(consumer_thread, NULL);

    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&not_full);
    pthread_cond_destroy(&not_empty);

    return 0;
}

避免条件变量使用过程中的常见错误

  1. 忘记加锁
    • 条件变量的操作(pthread_cond_waitpthread_cond_signal 等)必须在持有相关互斥锁的情况下进行。例如,在上述代码中,pthread_cond_waitpthread_cond_signal 都在 pthread_mutex_lockpthread_mutex_unlock 之间。
  2. 使用 while 循环检查条件
    • 不能使用 if 语句检查条件。因为 pthread_cond_wait 被唤醒后,可能是虚假唤醒(即使没有其他线程调用 pthread_cond_signalpthread_cond_broadcast 也可能被唤醒)。所以应该使用 while 循环来重新检查条件,如代码中的 while (count == BUFFER_SIZE)while (count == 0)
  3. 正确使用信号
    • 选择合适的信号方式。pthread_cond_signal 唤醒一个等待的线程,pthread_cond_broadcast 唤醒所有等待的线程。在生产者 - 消费者模型中,pthread_cond_signal 通常就足够了,因为每次生产或消费操作只会影响一个等待的线程。但如果有多个不同类型的等待线程,可能需要使用 pthread_cond_broadcast
  4. 资源清理
    • 在程序结束时,要正确销毁条件变量和互斥锁。如上述代码中的 pthread_mutex_destroypthread_cond_destroy 操作,避免资源泄漏。