自从C11 直接内置线程库后再也不用靠操作系统API裸奔写并发了但是呢这标准库给的多线程支持还是太基础了想整点高级活儿比如线程池、信号量啥的还得咱自己动手丰衣足食。比如面试聊到线程池面试官一问“说说原理”你张口就来“任务队列 线程队列循环分配呗”听着贼溜可真让你写代码立马原地懵圈——因为C的std::thread一旦跑完函数就自爆了根本没法复用那咋整别慌咱有妙招让每个线程启动后不是干一票就跑而是蹲在调度函数里死循环——有活就干没活就睡直到线程池喊“收工”才滚蛋这不就实现线程复用了嘛简直比小区门口的共享单车还环保整个线程池就俩核心积木任务队列Task Queue谁先来谁先干FIFO安排得明明白白。线程大军Thread Pool一群打工人24小时待命随时准备搬砖。而这俩之间的配合妥妥一个生产者-消费者模型——主线程往队列里塞任务生产者工作线程从队列里抢任务消费者。为了防止大家抢成一团乱麻咱祭出两大法宝一把mutex锁保证同一时间只有一个人能动队列一个条件变量condition_variable队列空了别傻等直接睡觉等有新任务来了我“啪”一下把你叫醒积木1SafeQueue —— 线程安全的任务队列你以为直接拿个std::queue就能上战场Too young多线程环境下它脆得跟薯片一样。所以咱得给它套上盔甲——封装成SafeQueue原理贼简单所有操作都先抢锁抢到才能动队列。你看这代码enqueue、dequeue、empty、size哪个不是裹着unique_lock出场就像你去ATM取钱门一关别人只能干瞪眼// thread_pool.h 里的 SafeQueue 实现 template typename T class SafeQueue { private: std::queueT m_queue; // 利用模板函数构造队列 std::mutex m_mutex; // 访问互斥信号量 public: SafeQueue() {} SafeQueue(SafeQueue other) {} ~SafeQueue() {} bool empty() { std::unique_lockstd::mutex lock(m_mutex); return m_queue.empty(); } int size() { std::unique_lockstd::mutex lock(m_mutex); return m_queue.size(); } // 队列添加元素 void enqueue(T t) { std::unique_lockstd::mutex lock(m_mutex); m_queue.emplace(t); // 安全入队稳如老狗 } // 队列取出元素 bool dequeue(T t) { std::unique_lockstd::mutex lock(m_mutex); if (m_queue.empty()) return false; t std::move(m_queue.front()); // 右值引用性能拉满 m_queue.pop(); return true; } };看明白没加锁 → 操作 → 自动解锁三步走安全又高效这SafeQueue就是咱线程池的“任务快递柜”谁来取件都得排队刷脸积木2ThreadPool —— 真正的“包工头”提交任务submit()给你安排得明明白白你想往线程池扔个lambda成员函数普通函数带参数带返回值统统接得住秘诀就是C11的语法糖三件套可变模板参数Args...参数多少都不怕尾置返回类型- decltype(...)自动推导返回值类型再也不用手动写typename std::result_of...::type那种绕死人的玩意儿完美转发std::forward左值右值原样传递绝不掉帧重点来了为了让任务能异步执行并拿到结果咱用std::packaged_task把函数打包再用shared_ptr包一层方便复制最后塞进一个void()的lambda里丢进队列。为啥要转成void()因为任务队列只认统一接口就像快递柜不管你寄的是手机还是泡面都得装进标准箱子里// Submit a function to be executed asynchronously by the pool template typename F, typename... Args auto submit(F f, Args ...args) - std::futuredecltype(f(args...)) { // 1. 绑定函数和参数 → func() std::functiondecltype(f(args...))() func std::bind(std::forwardF(f), std::forwardArgs(args)...); // 2. 打包成packaged_task带返回值的异步任务 auto task_ptr std::make_sharedstd::packaged_taskdecltype(f(args...))()(func); // 3. 包装成void()函数塞进队列 std::functionvoid() warpper_func [task_ptr]() { (*task_ptr)(); }; // 4. 队列通用安全封包函数并压入安全队列 m_queue.enqueue(warpper_func); // 5. 唤醒一个等待中的线程 m_conditional_lock.notify_one(); // 6. 返回future让你随时能查结果 return task_ptr-get_future(); }此时你看到decltype(f(args...))脑壳是不是嗡嗡的。别慌这其实就是告诉编译器“兄弟你先看看f(args...)返回啥类型我就按那个类型返回std::future”——智能推导省心省力内置打工人ThreadWorker每个线程启动后就化身ThreadWorker死循环干活检查线程池是否shutdown没关就继续加锁看队列有没有活没有就wait()睡觉有活赶紧dequeue出来解锁然后猛干class ThreadWorker // 内置线程工作类 { private: int m_id; // 工作id ThreadPool *m_pool; // 所属线程池 public: ThreadWorker(ThreadPool *pool, const int id) : m_pool(pool), m_id(id) {} void operator()() { std::functionvoid() func; bool dequeued; while (!m_pool-m_shutdown) { { std::unique_lockstd::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(); // 干活 } } };注意那个wait(lock)——它会在等待时自动释放锁等被唤醒后再重新加锁。这设计简直绝了既省CPU又防死锁堪称线程界的“节能睡眠模式”完整线程池代码直接复制就能跑来上主菜下面这份代码就是咱亲手搓出来的C11线程池全家桶包含所有细节连构造函数删除都给你安排明白了防拷贝防移动专治各种手滑// thread_pool.h #ifndef THREAD_POOL_H #define THREAD_POOL_H #include mutex #include queue #include functional #include future #include thread #include utility #include vector template typename T class SafeQueue { private: std::queueT m_queue; std::mutex m_mutex; public: SafeQueue() {} SafeQueue(SafeQueue other) {} ~SafeQueue() {} bool empty() { std::unique_lockstd::mutex lock(m_mutex); return m_queue.empty(); } int size() { std::unique_lockstd::mutex lock(m_mutex); return m_queue.size(); } void enqueue(T t) { std::unique_lockstd::mutex lock(m_mutex); m_queue.emplace(t); } bool dequeue(T t) { std::unique_lockstd::mutex lock(m_mutex); if (m_queue.empty()) return false; t std::move(m_queue.front()); m_queue.pop(); return true; } }; 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::functionvoid() func; bool dequeued; while (!m_pool-m_shutdown) { { std::unique_lockstd::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; SafeQueuestd::functionvoid() m_queue; std::vectorstd::thread m_threads; std::mutex m_conditional_mutex; std::condition_variable m_conditional_lock; public: ThreadPool(const int n_threads 4) : m_threads(std::vectorstd::thread(n_threads)), m_shutdown(false) {} ThreadPool(const ThreadPool ) delete; ThreadPool(ThreadPool ) delete; ThreadPool operator(const ThreadPool ) delete; ThreadPool operator(ThreadPool ) delete; void init() { for (int i 0; i m_threads.size(); i) { m_threads.at(i) std::thread(ThreadWorker(this, i)); } } void shutdown() { m_shutdown true; m_conditional_lock.notify_all(); // 全员下班 for (int i 0; i m_threads.size(); i) { if (m_threads.at(i).joinable()) { m_threads.at(i).join(); } } } template typename F, typename... Args auto submit(F f, Args ...args) - std::futuredecltype(f(args...)) { std::functiondecltype(f(args...))() func std::bind(std::forwardF(f), std::forwardArgs(args)...); auto task_ptr std::make_sharedstd::packaged_taskdecltype(f(args...))()(func); std::functionvoid() warpper_func [task_ptr]() { (*task_ptr)(); }; m_queue.enqueue(warpper_func); m_conditional_lock.notify_one(); return task_ptr-get_future(); } }; #endif测试样例让线程池动起来光说不练假把式下面这段测试代码直接验证咱的线程池能不能扛住高并发// test.cpp #include iostream #include random #include thread_pool.h std::random_device rd; std::mt19937 mt(rd()); std::uniform_int_distributionint dist(-1000, 1000); auto rnd std::bind(dist, mt); void simulate_hard_computation() { std::this_thread::sleep_for(std::chrono::milliseconds(2000 rnd())); } void multiply(const int a, const int b) { simulate_hard_computation(); std::cout a * b a * b std::endl; } void multiply_output(int out, const int a, const int b) { simulate_hard_computation(); out a * b; std::cout a * b out std::endl; } int multiply_return(const int a, const int b) { simulate_hard_computation(); return a * b; } void example() { ThreadPool pool(3); // 3个打工人 pool.init(); // 开工 // 提交30个乘法任务 for (int i 1; i 3; i) for (int j 1; j 10; j) pool.submit(multiply, i, j); // 带输出参数的任务 int output_ref; auto future1 pool.submit(multiply_output, std::ref(output_ref), 5, 6); future1.get(); // 等结果 std::cout Last result (by ref): output_ref std::endl; // 带返回值的任务 auto future2 pool.submit(multiply_return, 5, 3); int res future2.get(); std::cout Last result (by return): res std::endl; pool.shutdown(); // 收工 } int main() { example(); return 0; }跑起来你会发现30个乘法任务并发执行输出顺序乱七八糟正常但每个结果都对还能通过future.get()同步拿结果——异步提交 同步获取灵活得一批深耕 C/C 开发的小伙伴如果你不知选择哪个方向就业发展纠结 C 的学习价值与行业前景不妨看为什么很多人劝退学 C但大厂核心岗位还是要 C帮你理清行业逻辑找准发展方向如果你想深耕Linux C/C后端却没有清晰的学习路径【大厂标准】Linux C/C 后端进阶学习路线给你大厂级的进阶指南少走弯路如果你想入局音视频流媒体领域零基础不知从何下手音视频流媒体高级开发 - 学习路线精准拆解学习重点搭建完整知识体系如果你瞄准桌面开发/嵌入式开发想掌握 C Qt 核心技能C Qt 学习路线一条龙桌面开发 嵌入式开发一站式教你从入门到实战如果你想挑战Linux内核开发追求硬核技术提升Linux 内核学习指南硬核修炼手册带你吃透内核底层突破技术瓶颈如果你正备战面试急需刷题提分C/C 高频八股文面试题 1000 题三直击面试高频考点帮你快速查漏补缺如果你面试总被手撕代码难住尤其是线程池相关考点手撕线程池C 程序员的能力试金石手把手教你实现核心逻辑夯实编程功底如果你着急找工作、无面试机会、拿不到offer分不清是技术欠缺还是简历问题更要从这些文章里找准问题核心针对性提升快速突破求职瓶颈总结线程池这东西说白了就是管理任务和线程的配对掌握核心原理剩下的都是语法糖这套线程池虽然只有百来行但五脏俱全支持任意函数、任意参数、返回值获取、线程安全、资源自动回收……作为C并发入门真的很合适不过老铁们注意啊实际项目中还得考虑异常安全、任务拒绝策略、动态扩缩容啥的。但作为基础框架它已经把核心思想给你焊死了——复用线程 安全队列 条件唤醒三大心法缺一不可