MST

星途 面试题库

面试题:C语言Linux多线程服务器模型下信号量实现复杂同步场景

在一个复杂的Linux C语言多线程服务器模型中,有多个线程组,不同线程组之间存在依赖关系,例如线程组A完成某些任务后,线程组B才能开始,且线程组B内的线程需要按特定顺序访问共享资源。请设计一种基于信号量的线程同步机制来满足此需求,并详细阐述设计思路,给出关键代码片段及对信号量操作的详细解释。
46.9万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 线程组间同步:使用信号量来表示线程组A任务的完成情况。当线程组A完成任务后,释放一个信号量,线程组B在开始前等待这个信号量。
  2. 线程组内线程同步:在线程组B内,对于共享资源的访问,使用另一个信号量来控制按特定顺序访问。每个线程在访问共享资源前等待相应的信号量,访问完成后释放下一个线程需要的信号量。

关键代码片段

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

// 定义两个信号量,用于线程组间和线程组内同步
sem_t group_sync_sem;
sem_t inner_group_sem;

// 线程组A的任务函数
void* groupA_task(void* arg) {
    // 模拟线程组A的任务
    printf("Thread group A is working...\n");
    // 任务完成后释放线程组间同步信号量
    sem_post(&group_sync_sem);
    return NULL;
}

// 线程组B的任务函数
void* groupB_task(void* arg) {
    int thread_id = *((int*)arg);
    // 等待线程组间同步信号量
    sem_wait(&group_sync_sem);

    // 等待线程组内同步信号量(根据特定顺序)
    if (thread_id == 0) {
        // 第一个线程不需要等待组内同步信号量
        printf("Thread %d in group B starts first.\n", thread_id);
        // 访问共享资源
        // 访问完成后释放下一个线程需要的组内同步信号量
        sem_post(&inner_group_sem);
    } else {
        sem_wait(&inner_group_sem);
        printf("Thread %d in group B starts.\n", thread_id);
        // 访问共享资源
        // 访问完成后释放下一个线程需要的组内同步信号量(如果有)
        if (thread_id < 2) {
            sem_post(&inner_group_sem);
        }
    }

    return NULL;
}

信号量操作详细解释

  1. sem_t group_sync_sem;:定义用于线程组间同步的信号量。

    • 初始化:在主线程中使用 sem_init(&group_sync_sem, 0, 0); 进行初始化,初始值为0,意味着线程组B开始时需要等待线程组A释放信号量。
    • sem_wait(&group_sync_sem);:在线程组B的任务函数中,线程等待这个信号量。如果信号量值为0,线程将阻塞,直到线程组A调用 sem_post(&group_sync_sem); 释放信号量。
    • sem_post(&group_sync_sem);:在线程组A的任务函数完成后,调用此函数释放信号量,通知线程组B可以开始。
  2. sem_t inner_group_sem;:定义用于线程组B内线程同步的信号量。

    • 初始化:在主线程中使用 sem_init(&inner_group_sem, 0, 0); 进行初始化,初始值为0,意味着除第一个线程外,其他线程需要等待前一个线程释放信号量。
    • sem_wait(&inner_group_sem);:在线程组B内除第一个线程外的其他线程中,等待此信号量。当信号量值为0时,线程阻塞,直到前一个线程完成共享资源访问并调用 sem_post(&inner_group_sem); 释放信号量。
    • sem_post(&inner_group_sem);:在线程组B内每个线程完成共享资源访问后,释放此信号量,允许下一个线程开始访问(如果有下一个线程)。

在主线程中,需要创建线程组A和线程组B的线程,并在程序结束时使用 sem_destroy(&group_sync_sem);sem_destroy(&inner_group_sem); 销毁信号量。

int main() {
    pthread_t groupA_thread;
    pthread_t groupB_threads[3];
    int thread_ids[3] = {0, 1, 2};

    // 初始化信号量
    sem_init(&group_sync_sem, 0, 0);
    sem_init(&inner_group_sem, 0, 0);

    // 创建线程组A的线程
    pthread_create(&groupA_thread, NULL, groupA_task, NULL);

    // 创建线程组B的线程
    for (int i = 0; i < 3; i++) {
        pthread_create(&groupB_threads[i], NULL, groupB_task, &thread_ids[i]);
    }

    // 等待线程组A和线程组B的线程完成
    pthread_join(groupA_thread, NULL);
    for (int i = 0; i < 3; i++) {
        pthread_join(groupB_threads[i], NULL);
    }

    // 销毁信号量
    sem_destroy(&group_sync_sem);
    sem_destroy(&inner_group_sem);

    return 0;
}