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

如何让ThreadPool等待所有任务完成?线程池阻塞与线程生命周期问题

线程池进程阻塞与主线程存活问题分析

问题描述

当线程池中的任务完成后,如果不调用shutdown()方法,整个进程会被阻塞。想了解在未调用thread.join()的情况下,如何保证主线程不会在工作线程之前被销毁?此前得到的结论是线程对象持有线程池的引用。

ThreadPool 实现代码

#pragma once

#include <functional>
#include <future>
#include <mutex>
#include <queue>
#include <thread>
#include <utility>
#include <vector>

#include "SafeQueue.h"

class ThreadPool {
private:
  class ThreadWorker {
  private:
    int m_id;
    ThreadPool * m_pool;
  public:
    ThreadWorker(ThreadPool * pool, const int id)
      : m_pool(pool), m_id(id) {
    }

    void operator()() {
      std::function<void()> func;
      bool dequeued;
      while (!m_pool->m_shutdown) {
        {
          std::unique_lock<std::mutex> lock(m_pool->m_conditional_mutex);
          if (m_pool->m_queue.empty()) {
            m_pool->m_conditional_lock.wait(lock);
          }
          dequeued = m_pool->m_queue.dequeue(func);
        }
        if (dequeued) {
          func();
        }
      }
    }
  };

  bool m_shutdown;
  SafeQueue<std::function<void()>> m_queue;
  std::vector<std::thread> m_threads;
  std::mutex m_conditional_mutex;
  std::condition_variable m_conditional_lock;
public:
  ThreadPool(const int n_threads)
    : m_threads(std::vector<std::thread>(n_threads)), m_shutdown(false) {
  }

  ThreadPool(const ThreadPool &) = delete;
  ThreadPool(ThreadPool &&) = delete;

  ThreadPool & operator=(const ThreadPool &) = delete;
  ThreadPool & operator=(ThreadPool &&) = delete;

  // Inits thread pool
  void init() {
    for (int i = 0; i < m_threads.size(); ++i) {
      m_threads[i] = std::thread(ThreadWorker(this, i));
    }
  }

  // Waits until threads finish their current task and shutdowns the pool
  void shutdown() {
    m_shutdown = true;
    m_conditional_lock.notify_all();
    
    for (int i = 0; i < m_threads.size(); ++i) {
      if(m_threads[i].joinable()) {
        m_threads[i].join();
      }
    }
  }

  // Submit a function to be executed asynchronously by the pool
  template<typename F, typename...Args>
  auto submit(F&& f, Args&&... args) -> std::future<decltype(f(args...))> {
    // Create a function with bounded parameters ready to execute
    std::function<decltype(f(args...))()> func = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
    // Encapsulate it into a shared ptr in order to be able to copy construct / assign 
    auto task_ptr = std::make_shared<std::packaged_task<decltype(f(args...))()>>(func);

    // Wrap packaged task into void function
    std::function<void()> wrapper_func = [task_ptr]() {
      (*task_ptr)(); 
    };

    // Enqueue generic wrapper function
    m_queue.enqueue(wrapper_func);

    // Wake up one thread if its waiting
    m_conditional_lock.notify_one();

    // Return future from promise
    return task_ptr->get_future();
  }
};

测试代码

int add(const int x, const int y) {
  return x + y;
}

int main() {
  ThreadPool pool(2);

  pool.init();
  auto ret = pool.submit(add, 1, 3);
  auto result = ret.get();
  std::cout << result << std::endl;
}

问题解答

进程阻塞的原因

当前线程池的工作线程在任务完成后,会进入m_conditional_lock.wait(lock)的等待状态——因为m_shutdown默认是false,工作线程不会退出循环,会一直阻塞在条件变量上。当主线程执行到main函数末尾时,局部对象pool会被析构,而pool中的std::thread对象仍处于可 joinable 状态(未调用join()或detach())。根据C++标准,这种情况下程序会调用std::terminate()终止进程,这就是你观察到的“进程阻塞”(实际是触发了强制终止逻辑)。

关于线程对象与线程池的引用关系

之前的结论存在偏差:std::thread对象本身并没有持有线程池的引用,而是工作线程的执行函数(ThreadWorker::operator())通过指针m_pool持有线程池的地址。但这和主线程是否被销毁没有直接关联——主线程的销毁(main函数结束)会触发局部对象的析构,核心矛盾在于std::thread对象的析构规则。

未调用join()时保证主线程安全的方案

  1. 调用std::thread::detach():将工作线程与线程对象分离,分离后的工作线程会成为后台线程,当主线程结束时,后台线程会被操作系统强制终止。但这种方式不适合线程池场景——分离后无法再管理工作线程,也无法安全地关闭线程池,会导致资源泄漏风险。
  2. 修改线程池析构函数自动调用shutdown():这是更合理的方案,在ThreadPool的析构函数中自动触发shutdown(),确保工作线程被正确等待并退出,避免进程被强制终止。添加如下代码到ThreadPool的public区域:
~ThreadPool() {
    if (!m_shutdown) {
        shutdown();
    }
}

这样即使忘记手动调用shutdown(),线程池析构时也会自动完成工作线程的回收,保证进程正常退出。

总结

线程池场景下,正确的做法始终是在主线程退出前调用shutdown()(或通过析构函数自动触发),确保工作线程完成任务后安全退出。依赖detach()的方案无法保证线程池资源的安全释放,不推荐使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 05:50:01