前言
- 线程池就像一个“工人队伍”,提前雇好几个工人(线程),让他们随时待命。每个任务来时,不用临时招人(创建新线程),直接让闲着的工人去干活复用线程。干完后,工人不走,继续等下一个任务。
线程池的应用场景:
-
线程池最适合那些需要“同时干多件事,但任务短小精悍”的地方。简单说,它像一个“待命工人队”,帮你高效处理并发任务。常见场景有这些(基于实际开发经验):
-
Web服务器处理请求:比如网站收到很多用户访问(如淘宝浏览商品),每个请求是短任务,用线程池分配“工人”快速响应,避免服务器卡顿。
-
文件上传/下载:云盘(如百度网盘)用户上传照片时,多个文件并行处理,但不让线程乱创建太多。
-
高并发业务如秒杀/抢票:网购秒杀(双11)或12306买票,瞬间海量请求,用线程池控制线程数,防止系统崩溃。
-
IO密集型任务:数据库查询或网络调用(等IO时间长),线程池让CPU多干活,提高效率。
-
定时/异步任务:后台定时发送邮件或处理日志,用线程池调度,避免阻塞主程序。
总之,只要有“并发但不长耗时”的任务,线程池都能帮大忙!适合服务器、APP后台等。
下面我们就来通过C/C++代码和Linux的发行版本unbuntu20.04来编写一个简单的线程池,需要用到两个步骤,
封装线程
我们需要先封装一个个体的线程包含线程的基本信息,和函数。然后再用一个线程池的类组合起来,形成我们想要的线程池任务队列

pthread基本实现:
using func_t = std::function<void()>;
int number=1; // bug
class Thread
{
public:
private:
pthread_t _tid; // 线程TID
string _name; // 线程名字
bool _isdetach; // 是否分离
bool _isrunning; // 是否运行
void *res; // 线程返回值
func_t _func; // 线程处理任务的回调函数
};
在这里的_func是一个智能包装器(std::function),它内部“指向”或“存储”了线程运行时要调用的函数(回调函数)
初始化线程
在线程池中,初始化线程需要线程池将一个任务也就是函数传给线程发_func,让该线程来调用这个函数,我们在线程类的构造函数内初始化,其它的值我们直接在类的内部初始化。
...
public:
Thread(func_t func):
_tid(0),
_isdetach(false),
_isrunning(false),
res(nullptr),
_func(func)
{
_name = "thread-" + std::to_string(number++);
}
...
开始执行
编写一个bool Start()函数,执行成功返回true,失败返回false
bool Start() {
if (_isrunning) return false;
int n=pthread_create(&_tid, nullptr, Routine, this);
if (n !=0) {
return false;
}
else {
return true;
}
}
这个Routine封装了我们后续分离线程的函数,Routine通过this指针来调用我们线程需要执行的任务
但是我们线程跑起来之后需要设置线程状态是否已经跑起来了
void EnableDetach() {
_isdetach = true;
}
void EnableRunning() {
_isrunning = true;
}
Routine函数实现
static void *Routine(void *args) // 属于类内的成员函数,默认包含this指针!
{
Thread *self=static_cast<Thread *>(args);
self->EnableRunning();
if (!self->_isdetach) self->Detach();
pthread_setname_np(self->_tid, self->_name.c_str());
self->_func(); // 回调处理
return nullptr;
}
这里pthread_setname_np 用于给线程分配一个可读名字,方便调试、监控和日志追踪,是线程池或多线程程序中常用的辅助工具,将self->_name.c_str()信息写进self->_tid对应的线程之中,当我们打印线程信息的时候会显示线程的名称
self->Detach();用于后续我们将线程分离开来,以便管理线程
这些函数都是会封装在我们的thread的类里面,为什么这里的Routine需要一个static给它设置成一个静态函数呢,
- pthread_create 原型:
int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*start_routine)(void *),void *arg);
这里用于接收函数的参数是void *(*start_routine)(void *),start_routine 必须是 函数指针,也就是:必须是 普通函数或静态函数,不能是非静态成员函数。
- C++ 类的成员函数问题
普通(非静态)成员函数的签名:void* Thread::Routine(void* args);实际上会隐式传入 this 指针,签名变成:void* Routine(Thread* this, void* args);多了一个 this 参数,和 pthread_create 要求的 void* (*)(void*) 不匹配直接传 &Thread::Routine 给 pthread_create 会报错,设置成静态成员函数就没有this指针就符合 pthread_create 要求
线程分离
我们需要实现一个线程分离,让线程池中的线程都分离开,不需要进程等待线程pthread_join,这样会更方便
void Detach() {
if (_isdetach) return;
if (_isrunning) pthread_detach(_tid);
EnableDetach();
}
我们这里的Detach()可以用来将线程标记为需要分离的线程,再后面开始运行线程之后会直接将线程分离
在使用线程分离时,我们需要将线程标记为分离状态才能在运行时被分离开来
thread th(function);
th.Detach();
th.Start();
以这种先标记,再执行的方式将线程分离。
线程中止
如果我们不在运行线程,我们可以自定义中止
bool Stop() {
if (_isrunning) {
int n = pthread_cancel(_tid);
if (n != 0) {
return false;
} else {
_isrunning = false;
return true;
}
}
return false;
}
这里我们需要先判断线程是否在运行,然后直接中止线程,没什么特别的这里不做过多详述,只是需要用到pthread_cancel
线程等待
如果我们不需要线程分离,那么我们需要用线程等待来回收线程
void Join() {
if (_isdetach) {
return;
}
int n = pthread_join(_tid, & res);
if (n != 0) {
cout<< "Join线程失败"<<endl;
} else {
cout<< "Join线程成功";<<endl;
}
}
这里也没什么特别的,不做过多叙述,只是需要用到pthread_join
thread完整代码
#ifndef _THREAD_H_
#define _THREAD_H_
#include <iostream>
#include <string>
#include <pthread.h>
#include <cstdio>
#include <cstring>
#include <functional>
using namespace std;
namespace ThreadModlue {
using namespace LogModule;
static uint32_t number = 1; // bug
class Thread {
using func_t = std:: function < void() > ; // 暂时这样写,完全够了
private:
void EnableDetach() {
_isdetach = true;
}
void EnableRunning() {
_isrunning = true;
}
static void * Routine(void * args) // 属于类内的成员函数,默认包含this指针!
{
Thread * self = static_cast < Thread * > (args);
self -> EnableRunning();
if (self -> _isdetach)
self -> Detach();
pthread_setname_np(self -> _tid, self -> _name.c_str());
self -> _func(); // 回调处理
return nullptr;
}
// bug
public:
Thread(func_t func): _tid(0),
_isdetach(false),
_isrunning(false),
res(nullptr),
_func(func) {
_name = "thread-" + std::to_string(number++);
}
void Detach() {
if (_isdetach)
return;
if (_isrunning)
pthread_detach(_tid);
EnableDetach();
}
std::string Name() {
return _name;
}
bool Start() {
if (_isrunning)
return false;
int n = pthread_create( & _tid, nullptr, Routine, this);
if (n != 0) {
return false;
} else {
return true;
}
}
bool Stop() {
if (_isrunning) {
int n = pthread_cancel(_tid);
if (n != 0) {
return false;
} else {
_isrunning = false;
return true;
}
}
return false;
}
void Join() {
if (_isdetach) {
return;
}
int n = pthread_join(_tid, & res);
if (n != 0) {
cout<< "Join线程失败"<<endl;
} else {
cout << "Join线程成功"<<endl;
}
}
~Thread() {}
private:
pthread_t _tid;
std::string _name;
bool _isdetach;
bool _isrunning;
void * res;
func_t _func;
};
}
#endif
封装线程池
我们封装线程池需要有几个我们需要控制的变量:线程个数,线程存储,任务存储
static const int gnum = 5;
template <typename T>
class ThreadPool
{
public:
ThreadPool(int num = gnum) : _num(num)
{
}
~ThreadPool()
{
}
private:
std::vector<Thread> _threads; //存储线程的对象的容器
int _num; // 线程池中,线程的个数
std::queue<T> _taskq; //存储任务的容器
};
这里我们设置了两个容器分别存储线程和任务,因为任务需要排队被执行所以使用queue
初始化线程池
将每个线程都赋予一个任务
ThreadPool(int num = gnum): _num(num) {
for (int i = 0; i < num; i++) {
_threads.emplace_back(this->HandlerTask());
}
}
在线程池的类中我们会封装一个函数HandlerTask()用于执行线程的函数,这个后面会讲解,这里的HandlerTask(),是用于线程任务的执行。
任务处理
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;
void HandlerTask() {
while (true) {
T t;
{
pthread_mutex_lock(mutex);
while (_taskq.empty()) {
pthread_cond_wait(&cond,&mutex);
}
// 一定有任务
t = _taskq.front(); // 从q中获取任务,任务已经是线程私有的了!!!
_taskq.pop();
pthread_mutex_unlock(mutex);
}
t(); // 处理任务,需要在临界区内部处理吗?1 0
}
}
先用线程调用HandlerTask(),会进入一个死循环,不断处理任务T,知道没有任务处理,停止线程继续等待任务。
但是如何让HandlerTask()开始执行呢我们直接调用每个thread的Start()函数
void Start() {
for (auto & thread: _threads) {
thread.Start();
}
}
添加任务
void PushTask(const T& task)
{
pthread_mutex_lock(&mutex);
_taskq.push(task);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
线程池中的线程只是执行HandlerTask(),负责执行线程池中queue<T> _taskq的任务,如果我们不往_taskq添加任务,就无输出。
完整代码
#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <queue>
#include "pthread.hpp"
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static const int gnum = 5;
template <typename T>
class ThreadPool
{
public:
ThreadPool(int num = gnum) : _num(num)
{
for (int i = 0; i < num; i++)
{
_threads.emplace_back([this]()
{ this->HandlerTask(); });
}
}
void Start()
{
for (auto &thread : _threads)
{
thread.Start();
}
}
void PushTask(const T &task)
{
pthread_mutex_lock(&mutex);
_taskq.push(task);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
void HandlerTask()
{
char name[128];
pthread_getname_np(pthread_self(), name, sizeof(name));
while (true)
{
T t;
{
pthread_mutex_lock(&mutex);
while (_taskq.empty())
{
pthread_cond_wait(&cond, &mutex);
}
// 一定有任务
t = _taskq.front(); // 从q中获取任务,任务已经是线程私有的了!!!
_taskq.pop();
pthread_mutex_unlock(&mutex);
}
t(); // 处理任务,需要在临界区内部处理吗?1 0
}
}
~ThreadPool()
{
}
private:
std::vector<Thread> _threads;
int _num; // 线程池中,线程的个数
std::queue<T> _taskq;
};
演示线程池
using namespace std;
#include <iostream>
#include <unistd.h>
#include "pthreadpool.hpp"
int num = 0;
void fun()
{
cout << "我是一个任务" << num << endl;
num++;
}
int main()
{
using func = function<void()>;
func t1 = fun;
ThreadPool<func> tp1;
while (1)
{
tp1.PushTask(fun);
tp1.PushTask(fun);
tp1.PushTask(fun);
tp1.PushTask(fun);
tp1.PushTask(fun);
tp1.Start();
sleep(1);
}
}
演示结果
root@hcss-ecs-f59a:/gch/code/HaoHao/learn3/day5# ./exe
我是一个任务0
我是一个任务1
我是一个任务2
我是一个任务3
我是一个任务4
......
此博客中代码健壮性还是有问题,但是个人认为这些琐碎的问题讲出来也没意思,所以就压缩了点内容,请各位见谅
376

被折叠的 条评论
为什么被折叠?



