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

使用C++11 condition_variable实现三线程轮流打印1-100时遇死锁问题

三个线程轮流打印1-100的死锁问题排查与解决

问题描述

我想用C++11的condition_variable实现三个线程轮流打印1到100的程序,但遇到了死锁。有时程序只输出1、2就停止,但用gdb调试时却能正常运行。我知道wait可以加谓词,但不想新增全局变量,不清楚代码问题出在哪?

原代码

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


std::condition_variable cv12;
std::condition_variable cv23;
std::condition_variable cv31;

std::mutex mtx;

void Print(int i) {
  while (i <= 100) {
    if (i % 3 == 1) {
      {
        std::unique_lock<std::mutex> l(mtx);
        cv31.wait(l);
        std::cout << i << std::endl;
      }

      cv12.notify_all();
    } else if (i % 3 == 2) {
      {
        std::unique_lock<std::mutex> l(mtx);
        cv12.wait(l);
        std::cout << i << std::endl;
      }

      cv23.notify_all();
    } else if (i % 3 == 0) {
      {
        std::unique_lock<std::mutex> l(mtx);
        cv23.wait(l);
        std::cout << i << std::endl;
      }

      cv31.notify_all();
    }
    i += 3;
  }
}

int main() {
  std::thread t1(Print, 1);
  std::thread t2(Print, 2);
  std::thread t3(Print, 3);

  cv31.notify_all();

  t1.join();
  t2.join();
  t3.join();

  return 0;
}

运行情况

运行命令:

$ g++ -g -o tmp tmp.cpp  && ./tmp

异常输出:

╰─$ ./tmp                
1
2

问题分析

  1. 通知丢失的竞态条件:主线程创建t1后,可能t1还未进入cv31.wait()的等待状态,主线程就调用了cv31.notify_all(),这个通知会被直接丢弃。后续t1进入等待状态后,再也不会被唤醒,导致整个程序死锁。
  2. 虚假唤醒:condition_variable::wait()可能被操作系统虚假唤醒(无对应通知的唤醒),此时线程会错误地执行打印逻辑,破坏顺序,后续等待逻辑也会陷入混乱。

解决方案

线程间同步必然需要共享状态来标识执行顺序,你可以选择将共享状态封装到类中(而非全局变量),结合wait的谓词版本避免虚假唤醒和通知丢失。

方案1:封装到类中(无全局变量)

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

class Printer {
public:
    Printer() : current_turn(1) {}

    void Print(int start) {
        for (int i = start; i <= 100; i += 3) {
            std::unique_lock<std::mutex> l(mtx_);
            // 计算当前线程对应的期望轮次:1对应start=1,2对应start=2,3对应start=3(因3%3=0)
            int expected = start % 3;
            if (expected == 0) expected = 3;
            // 等待直到轮到当前线程执行
            cv_.wait(l, [this, expected]() { return current_turn == expected; });
            std::cout << i << std::endl;
            // 更新轮次为下一个线程
            current_turn = current_turn % 3 + 1;
            cv_.notify_all();
        }
    }

private:
    std::condition_variable cv_;
    std::mutex mtx_;
    int current_turn; // 共享状态:当前轮到的线程对应的轮次(1、2、3)
};

int main() {
    Printer p;
    std::thread t1(&Printer::Print, &p, 1);
    std::thread t2(&Printer::Print, &p, 2);
    std::thread t3(&Printer::Print, &p, 3);

    t1.join();
    t2.join();
    t3.join();

    return 0;
}

方案2:全局共享状态(简化版)

如果不介意全局变量,也可以用更简洁的实现:

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

std::condition_variable cv;
std::mutex mtx;
int current_turn = 1; // 当前轮到的线程对应的轮次(1、2、3)

void Print(int start) {
    for (int i = start; i <= 100; i += 3) {
        std::unique_lock<std::mutex> l(mtx);
        int expected = start % 3;
        if (expected == 0) expected = 3;
        cv.wait(l, [expected]() { return current_turn == expected; });
        std::cout << i << std::endl;
        current_turn = current_turn % 3 + 1;
        cv.notify_all();
    }
}

int main() {
    std::thread t1(Print, 1);
    std::thread t2(Print, 2);
    std::thread t3(Print, 3);

    t1.join();
    t2.join();
    t3.join();

    return 0;
}

修改说明

  • 用单个condition_variable替代三个,简化同步逻辑,所有线程等待同一个条件变量。
  • 通过共享状态current_turn明确标识当前执行的线程,结合wait的谓词版本,确保只有轮到自己时才继续执行,彻底避免虚假唤醒和通知丢失。
  • 每个线程打印完成后更新轮次,通知所有等待线程,让下一个符合条件的线程执行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 05:48:13