You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

线程安全有界队列push/pop时线程等待异常排查求助

线程安全有界队列死锁问题排查与修复

你的代码存在多个核心逻辑错误,直接导致程序无法正常结束,以下是问题分析和修正方案:

核心错误点

1. 互斥锁使用时机完全错误

  • 调用pthread_cond_wait必须先持有对应的互斥锁,否则属于未定义行为。你的代码在调用pthread_cond_wait前未加锁,直接访问q->count也会引发竞态条件(多个线程同时读写count)。
  • 修改队列的操作(添加/删除节点)必须全程在锁保护下,你现在先判断q->head == NULL再加锁,这期间队列状态可能被其他线程修改,导致逻辑错误。

2. 条件变量的检查逻辑错误

  • 虚假唤醒问题:pthread_cond_wait可能被虚假唤醒(不是因条件满足被signal唤醒),必须用while循环检查条件,而非if。你的queue_pop用if (q->count == 0)判断,会导致虚假唤醒后直接执行后续逻辑,此时队列可能仍为空。
  • 唤醒条件判断错误:
    • push操作后,只有当队列从空变为非空时(即push前count为0),才需要唤醒等待empty条件的pop线程;你现在只要count!=0就唤醒,属于无效唤醒,还可能浪费资源。
    • pop操作后,只有当队列从满变为非满时(即pop前count为size),才需要唤醒等待full条件的push线程;你现在只要count!=size就唤醒,同样是无效唤醒。

3. 队列状态的访问未受锁保护

所有对q->count、q->head、q->tail的读写操作,必须在互斥锁的保护下执行,否则会出现多线程下的数据竞争,导致队列状态混乱。


修正后的代码实现

修正后的queue_push函数

bool queue_push(queue_t *q, void *elem) {
    if (q == NULL) {
        return false;
    }

    // 先加锁,再检查队列状态
    pthread_mutex_lock(&q->mutex);

    // 用while循环处理队列满的情况,防止虚假唤醒
    while (q->count == q->size) {
        pthread_cond_wait(&q->full, &q->mutex);
    }

    // 执行添加节点的逻辑
    node_t *new_node = malloc(sizeof(node_t));
    if (new_node == NULL) {
        pthread_mutex_unlock(&q->mutex);
        return false;
    }
    new_node->content = elem;
    new_node->next = NULL;
    new_node->prev = q->tail;

    if (q->head == NULL) { // 队列原本为空
        q->head = new_node;
        q->tail = new_node;
    } else {
        q->tail->next = new_node;
        q->tail = new_node;
    }
    q->count++;

    // 仅当队列从空变为非空时,唤醒等待empty的线程
    if (q->count == 1) {
        pthread_cond_signal(&q->empty);
    }

    pthread_mutex_unlock(&q->mutex);
    return true;
}

修正后的queue_pop函数

bool queue_pop(queue_t *q, void **elem) {
    if (q == NULL || elem == NULL) {
        return false;
    }

    // 先加锁,再检查队列状态
    pthread_mutex_lock(&q->mutex);

    // 用while循环处理队列空的情况,防止虚假唤醒
    while (q->count == 0) {
        pthread_cond_wait(&q->empty, &q->mutex);
    }

    // 执行删除节点的逻辑
    node_t *del_node = q->head;
    *elem = del_node->content;

    if (q->head == q->tail) { // 队列只剩一个节点
        q->head = NULL;
        q->tail = NULL;
    } else {
        q->head = q->head->next;
        q->head->prev = NULL;
    }
    free(del_node);
    q->count--;

    // 仅当队列从满变为非满时,唤醒等待full的线程
    if (q->count == q->size - 1) {
        pthread_cond_signal(&q->full);
    }

    pthread_mutex_unlock(&q->mutex);
    return true;
}

额外注意事项

  1. 队列初始化:必须正确初始化互斥锁和条件变量:
bool queue_init(queue_t *q, int size) {
    if (q == NULL || size <= 0) {
        return false;
    }
    q->head = NULL;
    q->tail = NULL;
    q->size = size;
    q->count = 0;
    pthread_mutex_init(&q->mutex, NULL);
    pthread_cond_init(&q->full, NULL);
    pthread_cond_init(&q->empty, NULL);
    return true;
}
  1. 销毁队列:使用完毕后要销毁互斥锁和条件变量,避免资源泄漏:
void queue_destroy(queue_t *q) {
    if (q == NULL) return;
    // 先清空所有节点
    node_t *curr = q->head;
    while (curr != NULL) {
        node_t *next = curr->next;
        free(curr);
        curr = next;
    }
    pthread_mutex_destroy(&q->mutex);
    pthread_cond_destroy(&q->full);
    pthread_cond_destroy(&q->empty);
}

内容的提问来源于stack exchange,提问作者MonstBlu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 09:10:44