目录
一,线程池设计
二,线程池应用场景
三,线程池准备
1,包装一个锁
2,一个任务类
三,线程池
1,成员介绍
2,设计单例模式
3,创建线程池
4,线程池执行的任务
5,线程池取任务和加任务
6,整体代码实现
四,线程池使用
五,谢谢各位佬的观看
一,线程池设计
我们这个简单线程池设计思想就是提前创建好一批线程,在没有任务的时候就在条件变量下等待,有任务了就唤醒线程去执行任务,执行完了之后不要释放,在条件变量下等待执行任务就好了。准点就在这里,线程创建好了之后执完任务不释放,这大大提高了效率。
我们把我们的main函数就当作一个创建任务的线程就好了,创建好了之后就不需要管了,唤醒线程池中等待的线程就好了,交给线程池去处理。线程池就不断的处理任务,处理完后继续等待。
二,线程池应用场景
就目前理解的话,线程池对于那种处理时间短的任务是非常赚的,频繁的短任务,用线程池处理完之后那个线程马上返回线程池继续处理下一个任务,因此如果是许多很长的任务的话就不太适合使用线程池,直接用普通的线程去处理就好了。
三,线程池准备
1,包装一个锁
包装一个生命周期随对象的锁,这里起始时练习一下智能指针的思想,可以完全不搞这一个的,但是我写了,就懒得在该回去了。其实也简单,就是在创建对象的时候在构造函数中加锁,对象处理作用域析构的时候我们在析构函数里面解锁。
#pragma once
#include <iostream>
class Mutex
{
public:
Mutex()
{
pthread_mutex_init(&lock_, nullptr);
}
~Mutex()
{
pthread_mutex_destroy(&lock_);
}
void lock()
{
pthread_mutex_lock(&lock_);
}
void unlock()
{
pthread_mutex_unlock(&lock_);
}
private:
pthread_mutex_t lock_;
};
// 让锁的生命周期最对象的生命周期。
class LockGuard
{
public:
// 构造对象的时候加锁。
LockGuard(Mutex* mutex)
: mutex_(mutex)
{
mutex_->lock();
}
~LockGuard()
{
mutex_->unlock();
}
private:
Mutex *mutex_;
};
2,一个任务类
不然创建任务就是插入数据,处理任务就是拿数据有点太单调了,我们这里任务类不写了,用上一节写的处理+-*/%的简答网络计算器。
#pragma once
#include <iostream>
#include <string>
// 一个任务类,并且他自己实现了处理任务的方法。
class Task
{
public:
Task(int one = 1, int two = 1, char op = '*')
: one_(one), two_(two), op_(op)
{
}
// 析构不需要写。
// 处理任务
int run()
{
int resault = -1;
switch (op_)
{
case '+':
resault = one_ + two_;
break;
case '-':
resault = one_ - two_;
break;
case '*':
resault = one_ * two_;
break;
case '/':
if (two_ == 0)
{
resault = -1;
flag = false;
}
else
{
resault = one_ / two_;
}
break;
case '%':
if (two_ == 0)
{
resault = -1;
flag = false;
}
else
{
resault = one_ % two_;
}
break;
default:
flag = false;
resault = -1;
break;
}
return resault;
}
// 用算符重载。
int operator() ()
{
return run();
}
// 把需要处理的任务用输出型参数带回,方便打印。
void getdata(int *one, int *two, char *op)
{
*one = one_;
*two = two_;
*op = op_;
}
bool istrue()
{
return flag;
}
private:
int one_;
int two_;
char op_;
bool flag = true;
};
三,线程池
1,成员介绍
我们需要一个互斥锁来保护临界资源。
一个条件变量在没有任务的时候让线程等待。
一个队列来保存任务。
一个标记位来确保线程不会应为多次启动线程池而创建多过的线程。
一个整形表示线程池中线程数量。
一个自己的static 指针,我们把线程池搞成单例模式。
2,设计单例模式
构造函数私有:
禁掉拷贝构造,赋值重载
留下一个接口来创建对象,
这个接口只会在第一次调用的时候创建一个对象,之后的调用只会返回这个对象的地址。
3,创建线程池
4,线程池执行的任务
5,线程池取任务和加任务
6,整体代码实现
#pragma once
#include <pthread.h>
#include <unistd.h>
#include <vector>
#include <queue>
#include <cassert>
#include "Lock.hpp"
#include"Task.hpp"
using namespace std;
template <class T>
class ThreadPool
{
public:
~ThreadPool()
{
pthread_mutex_destroy(&mutex_);
pthread_cond_destroy(&cond_);
}
// 准备搞成单例模式的。
ThreadPool(const ThreadPool<T> &in) = delete;
void operator=(const ThreadPool<T> &in) = delete;
// 单例模式一般通过一个接口获取对象,第一次获取是创建,之后直接返回对象的指针。
static ThreadPool<T> *getinstance()
{
static Mutex mutex;
if (instance_ == nullptr)
{
// 构建对象是自动加锁。
LockGuard LockGuard(&mutex);
if (instance_ == nullptr)
{
instance_ = new ThreadPool<T>();
}
}
// 处理作用域自动解锁。
return instance_;
}
//这里要传自己的参数,成员函数内定义线程函数一定要加 static;
static void *threadrooting(void* args)
{
pthread_detach(pthread_self());
ThreadPool<T> *dp = (ThreadPool<T>*)args;
while (true)
{
dp->LockQueue();
while (!dp->HaveTask())
{
dp->WaitForTask();
}
// 走到s这里,有任务的。
T tmp = dp->pop();
dp->unLockQueue();
int one;
int two;
char op;
tmp.getdata(&one,&two,&op);
cout<<"线程:"<<pthread_self()<<"执行任务:"<<one<<op<<two<<"="<<tmp()<<endl;
}
}
void start()
{
assert(!isstart_);
for (int i = 0; i < threadnum_; i++)
{
pthread_t t1;
pthread_create(&t1, nullptr, threadrooting,this);
}
}
// 这里不需要加锁了,因为线程处理方法那里加过锁了。注意了不要加不必要的锁影响效率。
T pop()
{
T tmp = Taskqueue_.front();
Taskqueue_.pop();
return tmp;
}
void push(const T& in)
{
LockQueue();
Taskqueue_.push(in);
unLockQueue();
//唤醒一个线程去处理这个任务。
AwakenForThread();
}
private:
ThreadPool(int threadnum = 5)
: threadnum_(threadnum), isstart_(false)
{
assert(threadnum > 0);
pthread_mutex_init(&mutex_, nullptr);
pthread_cond_init(&cond_, nullptr);
}
void LockQueue()
{
pthread_mutex_lock(&mutex_);
}
void unLockQueue()
{
pthread_mutex_unlock(&mutex_);
}
void WaitForTask()
{
//注意条件变量一定要加锁了,不然就完犊子了。
pthread_cond_wait(&cond_, &mutex_);
}
void AwakenForThread()
{
pthread_cond_signal(&cond_);
}
bool HaveTask()
{
return !Taskqueue_.empty();
}
private:
bool isstart_; // 标记第一次创建线程池。
queue<T> Taskqueue_;//保存任务
int threadnum_;//线程池线程个数
pthread_mutex_t mutex_;//互斥锁
pthread_cond_t cond_;//条件变量
// 搞一个单例模式。
static ThreadPool<T> *instance_;
};
template <class T>
ThreadPool<T> *ThreadPool<T>::instance_ = nullptr;
四,线程池使用
我们main函数作为一个创建任务的线程就好了,创建好了就加入线程池,线程池自己就执行任务了。
#include <iostream>
#include <ctime>
#include <string>
#include<thread>
#include "Task.hpp"
#include "ThreadPool.hpp"
using namespace std;
string ops = "+-*/%";
int main()
{
ThreadPool<Task>* dp;
dp=ThreadPool<Task>::getinstance();
//unique_ptr<ThreadPool<Task> > dp(ThreadPool<Task>::getinstance());
//启动线程池。
dp->start();
// 主进程相当于有一个派发任务的就好了,只管派发任务,人下的不需要管了。
while (true)
{
// 构建一个任务、
int one = rand() % 50;
int two = rand() % 50;
char op = ops[rand() % ops.size()];
Task t = Task(one, two, op);
cout << "生产了一个任务:"<<one<<op<<two<<"=?"<<endl;
dp->push(t);
sleep(1);
}
return 0;
}
结果展示
五,谢谢各位佬的观看
感谢各位佬观看,必须单独一章!!!。