MST

星途 面试题库

面试题:C语言匿名管道与多线程协作及资源管理

假设在一个复杂的Linux C项目中,需要使用匿名管道进行进程间通信,同时还涉及多线程操作。描述如何协调多线程与匿名管道之间的协作,以避免竞争条件和死锁问题。并且说明在这种情况下,如何进行有效的资源管理(如文件描述符、内存等),请提供完整的代码框架及关键部分的详细解释。
36.1万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

1. 协调多线程与匿名管道协作

  1. 避免竞争条件
    • 使用互斥锁(pthread_mutex_t)来保护对匿名管道的读写操作。在进行读或写操作前,先获取互斥锁,操作完成后释放互斥锁。
    • 对于多线程读操作,可以使用条件变量(pthread_cond_t)来通知线程有数据可读,避免无效的读操作竞争。
  2. 避免死锁
    • 按照固定顺序获取锁。例如,如果有多个互斥锁,所有线程都以相同顺序获取这些锁,避免循环依赖导致死锁。
    • 使用超时机制,在获取锁时设置一个超时时间,如果在规定时间内未能获取到锁,则放弃操作并进行相应处理。

2. 资源管理

  1. 文件描述符管理
    • 在父进程创建匿名管道后,将文件描述符传递给子线程。注意在子线程结束时,要正确关闭文件描述符,避免资源泄漏。
    • 可以使用fcntl函数设置文件描述符为非阻塞模式,这样在读写操作时可以避免线程阻塞。
  2. 内存管理
    • 使用动态内存分配函数(如malloccalloc)分配内存时,要确保在不再使用时使用相应的释放函数(如free)进行释放。
    • 对于多线程共享的内存区域,要使用互斥锁等同步机制来保护内存的读写操作。

3. 代码框架及关键部分解释

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>
#include <string.h>

#define BUFFER_SIZE 1024

// 定义全局变量
int pipe_fds[2];
pthread_mutex_t pipe_mutex;
pthread_cond_t data_ready;
char buffer[BUFFER_SIZE];

// 线程函数,用于写管道
void* write_to_pipe(void* arg) {
    // 加锁
    pthread_mutex_lock(&pipe_mutex);
    const char* message = "Hello from thread";
    write(pipe_fds[1], message, strlen(message));
    // 解锁并通知有数据可读
    pthread_cond_signal(&data_ready);
    pthread_mutex_unlock(&pipe_mutex);
    return NULL;
}

// 线程函数,用于读管道
void* read_from_pipe(void* arg) {
    // 加锁
    pthread_mutex_lock(&pipe_mutex);
    // 使用条件变量等待数据
    while (read(pipe_fds[0], buffer, BUFFER_SIZE) == 0) {
        pthread_cond_wait(&data_ready, &pipe_mutex);
    }
    printf("Read from pipe: %s\n", buffer);
    // 解锁
    pthread_mutex_unlock(&pipe_mutex);
    return NULL;
}

int main() {
    pthread_t write_thread, read_thread;

    // 初始化互斥锁和条件变量
    pthread_mutex_init(&pipe_mutex, NULL);
    pthread_cond_init(&data_ready, NULL);

    // 创建匿名管道
    if (pipe(pipe_fds) == -1) {
        perror("pipe");
        return 1;
    }

    // 设置管道读端为非阻塞模式
    int flags = fcntl(pipe_fds[0], F_GETFL, 0);
    fcntl(pipe_fds[0], F_SETFL, flags | O_NONBLOCK);

    // 创建写线程
    if (pthread_create(&write_thread, NULL, write_to_pipe, NULL) != 0) {
        perror("pthread_create write");
        return 1;
    }
    // 创建读线程
    if (pthread_create(&read_thread, NULL, read_from_pipe, NULL) != 0) {
        perror("pthread_create read");
        return 1;
    }

    // 等待线程结束
    pthread_join(write_thread, NULL);
    pthread_join(read_thread, NULL);

    // 关闭管道文件描述符
    close(pipe_fds[0]);
    close(pipe_fds[1]);

    // 销毁互斥锁和条件变量
    pthread_mutex_destroy(&pipe_mutex);
    pthread_cond_destroy(&data_ready);

    return 0;
}
  • 关键部分解释
    • 互斥锁和条件变量初始化:在main函数中,使用pthread_mutex_init初始化互斥锁pipe_mutex,使用pthread_cond_init初始化条件变量data_ready
    • 匿名管道创建:使用pipe函数创建匿名管道,返回的文件描述符存储在pipe_fds数组中,pipe_fds[0]为读端,pipe_fds[1]为写端。
    • 设置非阻塞模式:通过fcntl函数将管道读端设置为非阻塞模式,避免读操作阻塞线程。
    • 线程函数write_to_pipe线程函数在获取互斥锁后向管道写端写入数据,然后通过条件变量通知有数据可读;read_from_pipe线程函数在获取互斥锁后,使用条件变量等待数据,有数据时读取并打印。
    • 资源清理:在main函数结束前,关闭管道文件描述符,销毁互斥锁和条件变量,确保资源正确释放。