MST

星途 面试题库

面试题:C语言POSIX线程的复杂同步与数据共享

假设有一个C语言程序,使用POSIX线程库。存在一个共享的环形缓冲区,大小为10。有3个生产者线程,每个生产者线程向缓冲区中写入随机整数,有2个消费者线程,从缓冲区中读取整数并计算其平方。请实现这个程序,要求处理好线程同步、数据共享以及缓冲区满和空的情况。同时说明如何避免死锁。
26.4万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <time.h>

#define BUFFER_SIZE 10
#define PRODUCER_THREADS 3
#define CONSUMER_THREADS 2

int buffer[BUFFER_SIZE];
int in = 0;
int out = 0;

sem_t empty;
sem_t full;
pthread_mutex_t mutex;

// 生产者线程函数
void* producer(void* arg) {
    int id = *((int*)arg);
    while (1) {
        int num = rand() % 100; // 生成随机整数
        sem_wait(&empty); // 等待缓冲区有空闲位置
        pthread_mutex_lock(&mutex);
        buffer[in] = num;
        printf("Producer %d produced %d at position %d\n", id, num, in);
        in = (in + 1) % BUFFER_SIZE;
        pthread_mutex_unlock(&mutex);
        sem_post(&full); // 通知缓冲区有新数据
    }
    return NULL;
}

// 消费者线程函数
void* consumer(void* arg) {
    int id = *((int*)arg);
    while (1) {
        sem_wait(&full); // 等待缓冲区有数据
        pthread_mutex_lock(&mutex);
        int num = buffer[out];
        printf("Consumer %d consumed %d from position %d, squared value: %d\n", id, num, out, num * num);
        out = (out + 1) % BUFFER_SIZE;
        pthread_mutex_unlock(&mutex);
        sem_post(&empty); // 通知缓冲区有空闲位置
    }
    return NULL;
}

int main() {
    pthread_t producers[PRODUCER_THREADS];
    pthread_t consumers[CONSUMER_THREADS];
    int producer_ids[PRODUCER_THREADS];
    int consumer_ids[CONSUMER_THREADS];

    sem_init(&empty, 0, BUFFER_SIZE);
    sem_init(&full, 0, 0);
    pthread_mutex_init(&mutex, NULL);

    srand(time(NULL));

    for (int i = 0; i < PRODUCER_THREADS; i++) {
        producer_ids[i] = i;
        pthread_create(&producers[i], NULL, producer, &producer_ids[i]);
    }

    for (int i = 0; i < CONSUMER_THREADS; i++) {
        consumer_ids[i] = i;
        pthread_create(&consumers[i], NULL, consumer, &consumer_ids[i]);
    }

    for (int i = 0; i < PRODUCER_THREADS; i++) {
        pthread_join(producers[i], NULL);
    }

    for (int i = 0; i < CONSUMER_THREADS; i++) {
        pthread_join(consumers[i], NULL);
    }

    sem_destroy(&empty);
    sem_destroy(&full);
    pthread_mutex_destroy(&mutex);

    return 0;
}

如何避免死锁

  1. 顺序加锁:在上述代码中,生产者和消费者线程都是先获取emptyfull信号量,再获取互斥锁mutex,解锁时顺序相反。这保证了所有线程加锁的顺序一致,避免死锁。
  2. 资源分配图算法:可以使用如银行家算法等资源分配图算法来检测和避免死锁,但在这种简单场景下,上述顺序加锁的方式足以避免死锁。
  3. 避免嵌套锁:尽量减少锁的嵌套使用。如果必须嵌套,严格按照相同顺序获取锁。在本程序中,只有一个互斥锁mutex,且获取和释放逻辑简单清晰,减少了死锁风险。