线程池

文章目录

原理

在这里插入图片描述

如我们每次动态开辟一个变量空间的时候,都会有效率的损失,如果有一个内存池,需要的化直接在里面取就行了,这就大大的提高了效率

当我们创建任务的时候再去申请线程,就相当于我们需要malloc的时候再去申请空间,创建线程也是有成本的,

请求来了,线程要提前准备好,任务来了,就指派给他
提前准备好的线程,原来随时处理任务就叫做线程池
(提高效率)

实现

Task.hpp

使用这个Task类,将此作为里面的类型

Task.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#pragma once
#include <iostream>
#include <pthread.h>

namespace ns_task
//使用命名空间,提高代码的安全性
{
class Task
{
private:
int _x;
int _y;
char _op; //表示+-*/%
public:
Task() //无参构造,为了拿任务,不需要参数列表
{
}
//进行函数重载
Task(int x, int y, char op) //有参构造,制造任务
: _x(x), _y(y), _op(op)
{ }
~Task()//析构函数其实也不需要我们来实现,因为没有变量需要销毁
{
}
int Run()//task类,处理任务
{
int res = 0;
switch (_op)
{
case '+':
res = _x + _y;
break;
case '-':
res = _x - _y;
break;
case '*':
res = _x * _y;
break;
case '/':
res = _x / _y;
break;
case '%':
res = _x % _y;
break;
default:
std::cout << "bug?" << std::endl;
break;
}
std::cout << "当前任务正在被:" << pthread_self() << "处理:" << _x << _op << _y << "=" << res << std::endl;
return res;
}
Task operator=(Task &s)//赋值运算符重载
{
if (this != &s)
{
_x = s._x;
_y = s._y;
_op = s._op;
}
return *this;
}

//Task t;
//t()//
int operator()()//将()重载,仿函数
{
return Run();//()使用run()
}
};
}

threadpool.hpp

实现线程池类,高封装性,将所有的对于线程的操作都放在这个类里面是实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#pragma once
#include <iostream>
#include <string>
#include <queue>
#include<unistd.h>
#include<pthread.h>
// #cludine"Task.hpp"
// using namespace ns_task;
namespace ns_threadpool
{
const int g_num=5;//使用一个全局变量,作为我们需要创建的线程的个数
template <class T>//使用模板,可以实现泛型编程,多个类型都可以使用
class ThreadPool //线程池
{

private:
int num_; //一个线程池里面有多少个任务
std::queue<T> task_queue_; //任务队列,临界资源
pthread_mutex_t mtx_;//锁
pthread_cond_t cond_;//条件变量

public:
ThreadPool(int num=g_num) :num_(num)//初始化构造函数
{
pthread_mutex_init(&mtx_,nullptr);
pthread_cond_init(&cond_,nullptr);
}


~ThreadPool()
{
pthread_mutex_destroy(&mtx_);
pthread_cond_destroy(&cond_);
}

//在类中,要让

static void* Rountine(void* args)
//主线程里面往里面塞任务,而线程里面主要就是在里面处理任务
//也不能访问类里面非static成员
{

pthread_detach(pthread_self());//实现线程分离就不要再去join等待了
ThreadPool<T>* tp=(ThreadPool<T>*)args;//类型转换
while(true)
{
//从任务队列里面去拿一个任务
//执行任务,要先把这个任务队列锁主

//每个线程他跟放任务的线程一样,都是竞争式的去拿一个任务
tp->Lock();
//先检测任务队列是否有一个任务
while(tp->IsEmpty())
{
//检测到任务队列为空
//此时线程就挂起等待
tp->Wait();
}
//该任务队列里面一定有任务了
T t;
tp->PopTask(&t);
//任务就拿到了
tp->UnLock();
t.Run();//可能有多个线程在处理任务,

sleep(1);
}
}

//我们定义了一个线程池的变量,就需要首先先进行初始化
void InitThreadPool()
{
//初始化一批线程,
//这样就不要每次用都要去开辟线程了
pthread_t tid;//一次创建一批线程
for(int i=0;i<num_;i++)
{
pthread_create(&tid,nullptr,Rountine,(void*)this);//在create里面实现函数,所以我们需要在类里面实现这个函数
//在类中不能执行线程的方法,因为他都有隐藏的this指针
//所以我们需要使用静态的函数,就没有了this指针
}
}

void PopTask(T* out)//头删,在线程池里面取出任务
{
*out=task_queue_.front();
task_queue_.pop();
}
void Wait()
{
pthread_cond_wait(&cond_,&mtx_);
}
bool IsEmpty()
{
return task_queue_.empty();
}
void Lock()
{
pthread_mutex_lock(&mtx_);
}
void UnLock()
{
pthread_mutex_unlock(&mtx_);
}
void Wakeup()
{
pthread_cond_signal(&cond_);
}

//我们定义了一个变量之后,初始化之后,就要向里面放任务,然后里面的线程,通过里面的队列,争抢任务

void PushTask(const T & in)
{
//塞任务,就相当于一个生产者,生产者之间要进行互斥访问
Lock();
task_queue_.push(in);
UnLock();
Wakeup();//塞完任务之后,就要唤醒里面的队列,来取任务
}

//万一任务队列里面一个任务都没有的话,那么线程池里面的每一个线程就要处于休眠状态,挂起等待

};
}

main.cc

非单例模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

#include"threadpool.hpp"
#include"Task.hpp"
#include<unistd.h>
#include<cstdlib>
#include<ctime>
using namespace ns_task;//使用我们写的两个命名空间,提高了代码的封装性
using namespace ns_threadpool;

//线程池就是一个单例模式,只有一个线程池就够了

int main()
{
ThreadPool<Task>* tp=new ThreadPool<Task>();//我们一开始就创建一个线程池类型,线程池类型我们只需要一个就行了,
tp->InitThreadPool();
//我们希望是主线程不断的向线程池里面push任务,线程池里面竞争任务,处理这些任务
//外部可能存在一个或者多个线程向里面塞任务
srand((long long)time(nullptr));
while(true)
{
//以后就是从网络里面来
//主线程就是把任务放到线程池里面去

//有的时候访问网站,挂掉了,OS受不了了,杀掉

Task t(rand()%20+1,rand()%10+1,"+-*/%"[rand()%5]);
tp->PushTask(t);//放进去让他进行处理工作



}

return 0;
}

详细代码可以查看
ThreadPool


线程池
http://example.com/2022/06/03/线程池/
作者
Zevin
发布于
2022年6月3日
许可协议