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

C++双线程打印向量奇偶元素程序无输出且死锁问题排查

多线程打印奇偶元素程序挂起问题排查与修复

问题场景

面试要求实现C++程序,启动两个线程处理输入vector:一个线程打印偶数,另一个打印奇数。以下是原实现代码,但程序无输出且无限挂起:

std::mutex mu_vec;
std::condition_variable m_cv;
bool odd = true;

void print_odd(const std::vector<int>& vals, int &id){
    while(true){
        std::unique_lock sl(mu_vec);
        m_cv.wait(sl, []{return odd; });
        if(id >= vals.size()){
            sl.unlock();
            break;
        }
        if(vals[id] & 1){
            std::cout << vals[id] << "\t";
            id++;
        }
        else{
            odd = false;
            sl.unlock();
            m_cv.notify_one();
        }
    }
}

void print_even(const std::vector<int>& vals, int &id){
    while(true){
        std::unique_lock sl(mu_vec);
        m_cv.wait(sl, []{return !odd; });
        if(id >= vals.size()){
            sl.unlock();
            break;
        }
        if((vals[id] & 1) == 0){
            std::cout << vals[id] << "\t";
            id++;
        }
        else{
            odd = true;
            sl.unlock();
            m_cv.notify_one();
        }
    }
}

int main(){
    std::vector<int> vals{5, 10, 4, 7, 1, 6};
    int id = 0;
    std::jthread odd_printer(print_odd, std::cref(vals), std::ref(id));
    std::jthread eve_printer(print_even, std::cref(vals), std::ref(id));
    return 0;
}

问题根源

  1. 处理符合条件元素后无线程切换逻辑:比如奇数线程打印完奇数并id++后,没有切换odd标志位,也没有通知偶数线程,导致两个线程都进入等待状态。
  2. 锁操作冗余且错误:手动调用sl.unlock()会导致unique_lock后续行为异常,因为unique_lock本身会在作用域结束时自动释放锁。
  3. 线程退出未通知对方:当id超出vector长度时,当前线程直接退出,但未通知另一个线程,导致对方一直阻塞在wait,无法退出。

修复后的代码

#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
#include <condition_variable>

std::mutex mu_vec;
std::condition_variable m_cv;
bool odd_turn = true; // 标记当前是否轮到奇数线程执行

void print_odd(const std::vector<int>& vals, int &id){
    while(true){
        std::unique_lock<std::mutex> sl(mu_vec);
        // 等待轮到奇数线程,或id已超出范围
        m_cv.wait(sl, []{return odd_turn || id >= vals.size(); });
        
        if(id >= vals.size()){
            // 通知偶数线程退出
            odd_turn = false;
            m_cv.notify_one();
            break;
        }
        
        if(vals[id] & 1){
            std::cout << vals[id] << "\t";
            id++;
            // 切换到偶数线程
            odd_turn = false;
            m_cv.notify_one();
        } else {
            // 当前元素是偶数,切换到偶数线程
            odd_turn = false;
            m_cv.notify_one();
        }
        // unique_lock自动释放锁
    }
}

void print_even(const std::vector<int>& vals, int &id){
    while(true){
        std::unique_lock<std::mutex> sl(mu_vec);
        // 等待轮到偶数线程,或id已超出范围
        m_cv.wait(sl, []{return !odd_turn || id >= vals.size(); });
        
        if(id >= vals.size()){
            // 通知奇数线程退出
            odd_turn = true;
            m_cv.notify_one();
            break;
        }
        
        if((vals[id] & 1) == 0){
            std::cout << vals[id] << "\t";
            id++;
            // 切换到奇数线程
            odd_turn = true;
            m_cv.notify_one();
        } else {
            // 当前元素是奇数,切换到奇数线程
            odd_turn = true;
            m_cv.notify_one();
        }
        // unique_lock自动释放锁
    }
}

int main(){
    std::vector<int> vals{5, 10, 4, 7, 1, 6};
    int id = 0;
    std::jthread odd_printer(print_odd, std::cref(vals), std::ref(id));
    std::jthread eve_printer(print_even, std::cref(vals), std::ref(id));
    
    // 等待线程结束,jthread会自动join,这里加换行让输出更整洁
    std::cout << std::endl;
    return 0;
}

关键修改说明

  • 移除手动unlock调用,依赖unique_lock的自动释放机制,避免锁状态混乱。
  • 无论当前线程是否处理了元素,都要切换odd_turn标志并通知对方线程,确保线程交替执行。
  • 当id超出vector长度时,设置标志并通知对方线程,让对方能检测到退出条件并终止。
  • 调整wait的条件,加入id >= vals.size(),确保线程能在退出条件满足时被唤醒。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 13:59:44