MST

星途 面试题库

面试题:C语言Linux共享内存同步中条件变量的深度应用

在C语言的Linux共享内存编程场景里,条件变量是实现复杂同步逻辑的重要工具。假设多个进程需要根据共享内存中的数据状态进行不同操作,有些进程等待特定条件满足才进行处理。请阐述如何利用条件变量结合互斥锁实现这种同步逻辑,分析条件变量相较于信号量在这种场景下的优势,并给出具体的代码示例和对关键代码行的解释。
25.0万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

1. 利用条件变量结合互斥锁实现同步逻辑

在C语言的Linux共享内存编程中,结合条件变量和互斥锁实现同步逻辑的步骤如下:

  1. 初始化互斥锁和条件变量
    • 使用 pthread_mutex_init 初始化互斥锁。
    • 使用 pthread_cond_init 初始化条件变量。
  2. 进程获取互斥锁:在访问共享内存前,每个进程都要先获取互斥锁,以确保同一时间只有一个进程能访问共享内存。
  3. 检查共享内存数据状态:获取互斥锁后,进程检查共享内存中的数据状态,判断是否满足特定条件。
  4. 等待条件变量:如果条件不满足,进程调用 pthread_cond_wait 等待条件变量。pthread_cond_wait 会自动释放互斥锁,使其他进程能够访问共享内存。当条件满足被唤醒时,pthread_cond_wait 会重新获取互斥锁。
  5. 满足条件后进行处理:当条件满足时,进程进行相应的处理,处理完成后释放互斥锁。

2. 条件变量相较于信号量在这种场景下的优势

  • 精准唤醒:条件变量可以精准地唤醒等待特定条件的线程,而信号量是一种更通用的计数器,唤醒的线程不一定是等待特定条件的,可能需要额外的判断。
  • 避免虚假唤醒:虽然条件变量也可能出现虚假唤醒,但通过合理的编程(如在循环中检查条件),可以有效避免这种情况。而信号量在某些复杂场景下,可能更容易出现资源竞争和误唤醒的问题。
  • 与互斥锁紧密结合:条件变量通常与互斥锁紧密配合使用,更适合在需要保护共享资源并等待特定条件的场景中,而信号量更侧重于资源计数和一般的同步控制。

3. 具体代码示例及关键代码行解释

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define SHM_SIZE 1024

// 共享内存结构体
typedef struct {
    int data;
    pthread_mutex_t mutex;
    pthread_cond_t cond;
} SharedData;

// 生产者线程函数
void* producer(void* arg) {
    SharedData* shared = (SharedData*)arg;
    for (int i = 0; i < 5; ++i) {
        pthread_mutex_lock(&shared->mutex);
        shared->data = i;
        printf("Producer set data to %d\n", i);
        pthread_cond_signal(&shared->cond);
        pthread_mutex_unlock(&shared->mutex);
        sleep(1);
    }
    return NULL;
}

// 消费者线程函数
void* consumer(void* arg) {
    SharedData* shared = (SharedData*)arg;
    for (int i = 0; i < 5; ++i) {
        pthread_mutex_lock(&shared->mutex);
        while (shared->data < i) {
            pthread_cond_wait(&shared->cond, &shared->mutex);
        }
        printf("Consumer got data %d\n", shared->data);
        pthread_mutex_unlock(&shared->mutex);
    }
    return NULL;
}

int main() {
    int shm_fd;
    SharedData* shared;

    // 创建共享内存对象
    shm_fd = shm_open("/shared_memory", O_CREAT | O_RDWR, 0666);
    if (shm_fd == -1) {
        perror("shm_open");
        return 1;
    }

    // 设置共享内存大小
    if (ftruncate(shm_fd, SHM_SIZE) == -1) {
        perror("ftruncate");
        return 1;
    }

    // 映射共享内存到进程地址空间
    shared = (SharedData*)mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
    if (shared == MAP_FAILED) {
        perror("mmap");
        return 1;
    }

    // 初始化互斥锁和条件变量
    pthread_mutex_init(&shared->mutex, NULL);
    pthread_cond_init(&shared->cond, NULL);

    pthread_t producer_thread, consumer_thread;

    // 创建生产者和消费者线程
    if (pthread_create(&producer_thread, NULL, producer, shared) != 0) {
        perror("pthread_create producer");
        return 1;
    }
    if (pthread_create(&consumer_thread, NULL, consumer, shared) != 0) {
        perror("pthread_create consumer");
        return 1;
    }

    // 等待线程结束
    pthread_join(producer_thread, NULL);
    pthread_join(consumer_thread, NULL);

    // 销毁互斥锁和条件变量
    pthread_mutex_destroy(&shared->mutex);
    pthread_cond_destroy(&shared->cond);

    // 取消映射并关闭共享内存
    if (munmap(shared, SHM_SIZE) == -1) {
        perror("munmap");
        return 1;
    }
    if (close(shm_fd) == -1) {
        perror("close");
        return 1;
    }
    if (shm_unlink("/shared_memory") == -1) {
        perror("shm_unlink");
        return 1;
    }

    return 0;
}

关键代码行解释

  • pthread_mutex_lock(&shared->mutex);:获取互斥锁,保护共享内存的访问。
  • while (shared->data < i) { pthread_cond_wait(&shared->cond, &shared->mutex); }:消费者线程检查条件,不满足则等待条件变量,pthread_cond_wait 会自动释放互斥锁并在唤醒时重新获取。
  • pthread_cond_signal(&shared->cond);:生产者线程设置数据后,发送信号唤醒等待的消费者线程。
  • pthread_mutex_unlock(&shared->mutex);:操作完成后释放互斥锁。
  • pthread_mutex_init(&shared->mutex, NULL);pthread_cond_init(&shared->cond, NULL);:初始化互斥锁和条件变量。
  • pthread_mutex_destroy(&shared->mutex);pthread_cond_destroy(&shared->cond);:销毁互斥锁和条件变量。