线程池的概念
比如我们去海底捞去吃饭,老板不可能来一个客人招募一个服务员,而是先招募一批服务员来服务即将到来的顾客,线程池也是这样,当我们要使用线程处理数据的时候,如果每来一批数据我们创建一个线程的话,每次创建线程的代价是十分昂贵的,分配内存,陷入内核,列入调度等等,不仅如此这些线程还不方便调度如果我们一次性创建多个线程,再分配线程来处理数据,这样就避免了创建很多线程的开销,而且还方便调度这些线程。
线程池的关键元素
- 线程:核心线程和工作线程
- 任务队列:用于待执行任务排队
- 互斥量:因为每个线程都访问任务队列,这是一个临界资源要被互斥量进行保护
- 条件变量:当队列是空的时候,这时线程不能拿数据,所以就需要条件变量进行判断
各个程序的作用
main.cc:用于创建线程池,并塞进任务
#include "thread_pool.hpp"
#include "task.hpp"
#include <time.h>
#include <cstdlib>
#include <unistd.h>
using namespace ns_task;
using namespace ns_thread_pool;
int main()
{
thread_pool<Task> *tp = new thread_pool<Task>(10);//创建十个线程
tp->InitThreadPool();//初始化线程
// tp->
srand((long long)time(nullptr));//初始化随机数种子
while (true)
{
Task t(rand() % 20 + 1, rand() % 10 + 1, "+-*/%"[rand() % 5]);//创建任务
tp->pushTask(t);//塞入任务
// sleep(1);
}
}
thread_pool.hpp:包含各种有用的函数
#pragma once
#include<queue>
#include<pthread.h>
#include"task.hpp"
using namespace ns_task;
namespace ns_thread_pool
{
const int num_default=5;
template<class T>
class thread_pool
{
private:
int num_;
std::queue<T> task_queue;
pthread_mutex_t mtx_;
pthread_cond_t cond_;
public:
void InitThreadPool()
{
//创建一些线程,调度线程,让每个线程去执行任务
pthread_t tid;
for (int i = 0; i < num_; i++)
{
pthread_create(&tid, nullptr, Rountine, (void *)this /*?*/);
}
}
void Lock()
{
pthread_mutex_lock(&mtx_);
}
void Unlock()
{
pthread_mutex_unlock(&mtx_);
}
void wait()
{
pthread_cond_wait(&cond_,&mtx_);
}
void signal()
{
pthread_cond_signal(&cond_);
}
void Wakeup()
{
pthread_cond_signal(&cond_);
//在条件变量下唤醒线程
}
void pushTask(const T& in)
{
Lock();
task_queue.push(in);
Unlock();
Wakeup();//唤醒线程
}
void popTask(T* out)
{
*out=task_queue.front();
task_queue.pop();
}
bool IsEmpty()
{
return task_queue.empty();
}
bool IsFull()
{
return task_queue.size()==num_;
}
//类中的成员函数调用另一个函数调用,会隐式传递一个this指针,要消除这个指针,要设为静态函数才行
//在类中执行类内成员方法,是不可行的
static void* Rountine(void* args)
{
pthread_detach(pthread_self());//线程分离
thread_pool<T>* tp=(thread_pool<T>*)args;
while(true)
{
tp->Lock();
while(tp->IsEmpty())
{
tp->wait();
//可能出现伪唤醒的情况,所以要用循环判断才行,如果队列是空的先让线程等一下
}
T t;
tp->popTask(&t);//让线程从任务队列中拿任务
tp->Unlock();//这个任务队列是被很多线程访问的,是临界资源,要被互斥量进行保护
t.Run();
}
}
// void Thread_init()
// {
// pthread_t tid;
// for(int i=0;i<num_;i++)
// {
// pthread_create(&tid,nullptr,Rountine,(void*)this);
// }
// }
thread_pool(int n=num_default):num_(n)
{
pthread_mutex_init(&mtx_,nullptr);
pthread_cond_init(&cond_,nullptr);
}
~thread_pool()
{
pthread_mutex_destroy(&mtx_);
pthread_cond_destroy(&cond_);
}
};
}
task.hpp:用于创建任务的头文件
#pragma once
#include <iostream>
#include <pthread.h>
namespace ns_task
{
class Task
{
private:
int x_;
int y_;
char op_; //+/*/%
public:
// void (*callback)();
Task() {}
Task(int x, int y, char op) : x_(x), y_(y), op_(op)
{
}
std::string Show()
{
std::string message = std::to_string(x_);
message += op_;
message += std::to_string(y_);
message += "=?";
return message;
}
int Run()
{
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;
}
int operator()()
{
return Run();
}
~Task() {}
};
}
makfile:自动编译文件
th_pool:main.cc
g++ -o $@ $^ -std=c++11 -lpthread
.PHONY:clean
clean:
rm -f th_pool
单例模式下的线程池
单例的概念
单例实际上就是一种创建对象的模式,这种模式涉及一个类,这种类可以实现创建一个对象,一旦这个对象被创建,那么后续再创建这个对象就不再创建,而是直接返回第一次被创建的对象,这种类提供了一种访问对象的唯一方式。可以直接访问,不需要实例化该类的对象。例如在很多服务器开发场景中, 经常需要让服务器加载很多的数据 (上百G) 到内存中. 此时往往要用一个单例的类来管理这
些数据.同时要注意,因为只能有一个对象被创建,拷贝构造函数和赋值是不被允许的。
对于我们的线程池,每个线程被创建都创建了任务队列,这样就是十分冗余的,我们其实用一个单例的类来管理这些数据就可以了。
懒汉和饿汉模式
懒汉实现单例模式:先把碗放下,然后下一顿用到碗再洗,这样的话就不会产生垃圾对象
template <typename T>
class Singleton {
static T* inst;
public:
static T* GetInstance() {
if (inst == NULL) {
inst = new T();
}
return inst;
}
};
解释:当对象没被创建,这个inst是空指针,那就创建一个对象,要是已经创建了,因为inst是静态的,那么就直接返回就可以了,但是缺点是不支持多线程,会存在线程安全的问题,因为第一次创建inst时要是多个一起创建,那就不安全了
饿汉模式:饿汉很饿,所以等不及,吃完饭立即洗碗,等待下一次吃饭,它的缺点是类创建的时候就初始化对象,产生垃圾对象
template <typename T>
class Singleton {
static T data;
public:
static T* GetInstance() {
return &data;
}
};
线程安全的懒汉线程池
#pragma once
#include<queue>
#include<pthread.h>
#include"task.hpp"
using namespace ns_task;
namespace ns_thread_pool
{
const int num_default=5;
template<class T>
class thread_pool
{
private:
int num_;
std::queue<T> task_queue;
pthread_mutex_t mtx_;
pthread_cond_t cond_;
static thread_pool<T>* inst;
//拷贝构造函数要被删除才行
thread_pool(int n=num_default):num_(n)
{
pthread_mutex_init(&mtx_,nullptr);
pthread_cond_init(&cond_,nullptr);
}
thread_pool(const thread_pool<T> &tp)=delete;
thread_pool& operator=(thread_pool<T>& tp)=delete;
public:
//这里要用static因为类成员函数调用另一个成员函数,要先有一个对象才行
static thread_pool<T>* Getinstance()
{
pthread_mutex_t mt=PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP;
//双判定可以让后来的线程不用在竞争锁,提高获取单例的效率
if(inst==nullptr)
{
pthread_mutex_lock(&mt);
if(inst==nullptr)
{
inst=new thread_pool<T>();
inst->InitThreadPool();
}
pthread_mutex_unlock(&mt);
}
return inst;
}
void InitThreadPool()
{
pthread_t tid;
for (int i = 0; i < num_; i++)
{
pthread_create(&tid, nullptr, Rountine, (void *)this /*?*/);
}
}
void Lock()
{
pthread_mutex_lock(&mtx_);
}
void Unlock()
{
pthread_mutex_unlock(&mtx_);
}
void wait()
{
pthread_cond_wait(&cond_,&mtx_);
}
void signal()
{
pthread_cond_signal(&cond_);
}
void Wakeup()
{
pthread_cond_signal(&cond_);
//在条件变量下唤醒线程
}
void pushTask(const T& in)
{
Lock();
task_queue.push(in);
Unlock();
Wakeup();//唤醒线程
}
void popTask(T* out)
{
*out=task_queue.front();
task_queue.pop();
}
bool IsEmpty()
{
return task_queue.empty();
}
bool IsFull()
{
return task_queue.size()==num_;
}
//类中的成员函数调用另一个函数调用,会隐式传递一个this指针,要消除这个指针,要设为静态函数才行
//在类中执行类内成员方法,是不可行的
static void* Rountine(void* args)
{
pthread_detach(pthread_self());
thread_pool<T>* tp=(thread_pool<T>*)args;
while(true)
{
tp->Lock();
while(tp->IsEmpty())
{
tp->wait();
//可能出现伪唤醒的情况
}
T t;
tp->popTask(&t);//从任务队列中拿任务
tp->Unlock();
t.Run();
}
}
// void Thread_init()
// {
// pthread_t tid;
// for(int i=0;i<num_;i++)
// {
// pthread_create(&tid,nullptr,Rountine,(void*)this);
// }
// }
~thread_pool()
{
pthread_mutex_destroy(&mtx_);
pthread_cond_destroy(&cond_);
}
};
template<class T>
thread_pool<T> *thread_pool<T>::inst=nullptr;
//静态成员要在类外初始化
}
#include "thread_pool.hpp"
#include "task.hpp"
#include <time.h>
#include <cstdlib>
#include <unistd.h>
using namespace ns_task;
using namespace ns_thread_pool;
int main()
{
std::cout<<"当前程序在执行其他代码"<<std::endl;
std::cout<<"当前程序在执行其他代码"<<std::endl;
std::cout<<"当前程序在执行其他代码"<<std::endl;
std::cout<<"当前程序在执行其他代码"<<std::endl;
//thread_pool<Task> *tp = new thread_pool<Task>(10);
//tp->InitThreadPool();
// tp->
srand((long long)time(nullptr));
while (true)
{
Task t(rand() % 20 + 1, rand() % 10 + 1, "+-*/%"[rand() % 5]);
thread_pool<Task>::Getinstance()->pushTask(t);
//tp->pushTask(t);
// sleep(1);
}
}