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

主线程等待子线程循环迭代完成?代码死锁问题排查

问题描述

我编写了两段C++多线程代码:

  1. 第一段中,主线程通过std::condition_variable通知子线程执行循环,但主线程不会等待所有子线程完成当前迭代就继续执行;
  2. 第二段尝试新增std::condition_variable和std::atomic计数器来实现等待逻辑,却导致程序完全死锁,仅能打印线程创建前的提示信息。

请问哪里出错了?该如何修正?

第一段代码

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

int main() {
  std::condition_variable myCV;
  std::mutex myMutex;
  bool isRunning{ true };

  const size_t numberOfThreads{ 6 };

  std::vector<std::jthread> threads;

  for (size_t i = 0; i < numberOfThreads; i++) {
    threads.emplace_back(
      [&isRunning, &myMutex, &myCV, i]() {
        std::unique_lock loopLock(myMutex);
        while (isRunning) {
          myCV.wait(loopLock);
          std::this_thread::sleep_for(std::chrono::milliseconds(500));
        }
        std::osyncstream oss{ std::cout };
        oss << "Ended thread " << i << std::endl;
      }
    );
  }


  for (int i = 0; i < 100; i++) {
    myCV.notify_all();
    std::this_thread::sleep_for(std::chrono::milliseconds(20));
    std::cout << "Test loop " << i << std::endl;
  }

  isRunning = false;
  myCV.notify_all();

  return 0;
}

第二段代码

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

int main() {
  std::condition_variable myCV;
  std::mutex myMutex;
  bool isRunning{ true };

  std::condition_variable loopEndCV;
  std::atomic<size_t> threadCounter{ 0 };
  std::mutex loopEndMutex;

  const size_t numberOfThreads{ 6 };

  std::vector<std::jthread> threads;

  std::cout << "Instantiating threads..." << std::endl;

  for (size_t i = 0; i < numberOfThreads; i++) {
    threads.emplace_back(
      [&, i]() {
        std::unique_lock loopLock(myMutex);
        while (isRunning) {
          myCV.wait(loopLock);
          std::this_thread::sleep_for(std::chrono::milliseconds(500));
          --threadCounter;
          if (threadCounter.load() == 0) {
            loopEndCV.notify_one();
          }
        }
        std::osyncstream oss{ std::cout };
        oss << "Ended thread " << i << std::endl;
      }
    );
  }


  for (int i = 0; i < 100; i++) {
    threadCounter = numberOfThreads;
    myCV.notify_all();
    std::unique_lock loopEndLock(loopEndMutex);

    loopEndCV.wait(loopEndLock, [&threadCounter]() { return threadCounter.load() == 0; });
    std::this_thread::sleep_for(std::chrono::milliseconds(20));
    std::cout << "Test loop " << i << std::endl;
  }

  isRunning = false;
  myCV.notify_all();

  return 0;
}

问题分析与修正方案

第一段代码的问题

主线程每次调用notify_all()后仅休眠20ms就进入下一轮循环,完全没有等待子线程完成当前迭代。子线程被唤醒后执行sleep_for(500ms)的过程中,主线程已经再次触发通知,导致子线程的迭代逻辑被频繁干扰,主线程与子线程的执行节奏完全脱节。

第二段代码死锁的原因

  1. 锁长时间被持有:子线程的std::unique_lock loopLock(myMutex)在整个while(isRunning)循环内持续持有锁,仅在wait()时临时释放。子线程被唤醒后执行sleep_for(500ms)期间仍持有锁,导致其他子线程无法获取锁完成唤醒后的逻辑,最终只有一个子线程能执行任务。
  2. 计数器逻辑失效:主线程设置threadCounter = numberOfThreads并通知后,由于多数子线程被锁阻塞,threadCounter永远无法减到0,主线程卡在loopEndCV.wait()中,形成死锁。

修正后的代码

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

int main() {
  std::condition_variable myCV;
  std::mutex myMutex;
  bool isRunning{ true };
  int currentIteration{ 0 }; // 用于避免虚假唤醒的迭代标记

  std::condition_variable loopEndCV;
  std::atomic<size_t> threadCounter{ 0 };
  std::mutex loopEndMutex;

  const size_t numberOfThreads{ 6 };

  std::vector<std::jthread> threads;

  std::cout << "Instantiating threads..." << std::endl;

  for (size_t i = 0; i < numberOfThreads; i++) {
    threads.emplace_back(
      [&, i]() {
        while (isRunning) {
          std::unique_lock loopLock(myMutex);
          // 等待主线程发起新迭代,同时处理虚假唤醒
          myCV.wait(loopLock, [&]() { return !isRunning || currentIteration > 0; });
          if (!isRunning) break;

          // 释放锁,让其他子线程能被唤醒执行
          loopLock.unlock();

          // 执行任务
          std::this_thread::sleep_for(std::chrono::milliseconds(500));
          std::osyncstream oss{ std::cout };
          oss << "Thread " << i << " finished iteration " << currentIteration << std::endl;

          // 最后一个子线程完成时通知主线程
          if (--threadCounter == 0) {
            std::unique_lock endLock(loopEndMutex);
            loopEndCV.notify_one();
          }
        }
        std::osyncstream oss{ std::cout };
        oss << "Ended thread " << i << std::endl;
      }
    );
  }


  for (int i = 1; i <= 100; i++) {
    {
      std::lock_guard lock(myMutex);
      currentIteration = i;
      threadCounter = numberOfThreads;
    }
    myCV.notify_all();

    std::unique_lock loopEndLock(loopEndMutex);
    // 等待所有子线程完成当前迭代
    loopEndCV.wait(loopEndLock, [&]() { return threadCounter == 0; });

    std::this_thread::sleep_for(std::chrono::milliseconds(20));
    std::cout << "Test loop " << i-1 << " completed" << std::endl;
  }

  {
    std::lock_guard lock(myMutex);
    isRunning = false;
    currentIteration = 0;
  }
  myCV.notify_all();

  return 0;
}

关键修复说明

  1. 及时释放锁:子线程被唤醒后立即释放myMutex,让所有子线程都能获取锁并执行任务,避免单线程独占锁。
  2. 迭代标记防虚假唤醒:新增currentIteration变量,确保子线程仅在主线程明确发起新迭代时才执行任务,避免条件变量的虚假唤醒干扰逻辑。
  3. 安全的计数器通知:利用原子变量threadCounter确保多线程计数更新安全,仅当最后一个子线程完成时才通知主线程。
  4. 状态更新加锁:修改currentIteration和isRunning时持有myMutex,保证子线程看到的状态一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 16:15:09