一、线程池
一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。
二、线程池的应用场景
1. 需要大量的线程来完成任务,且完成任务的时间比较短。WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。但对于长时间的任务,比如一个 Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。
2. 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
3. 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,出现错误.
4.线程池示例:
1. 创建固定数量线程池,循环从任务队列中获取任务对象,
2. 获取到任务对象后,执行任务对象中的任务接口.
三、代码
主线程发布任务,多线程获得任务,执行任务
(1)任务
Task.hpp
#pragma once
#include <iostream>
#include <string>
std::string opers="+-*/%";
enum{
DivZero=1,
ModZero,
Unknown
};
class Task
{
public:
Task(int x, int y, char op) : data1_(x), data2_(y), oper_(op), result_(0), exitcode_(0)
{
}
void run()
{
switch (oper_)
{
case '+':
result_ = data1_ + data2_;
break;
case '-':
result_ = data1_ - data2_;
break;
case '*':
result_ = data1_ * data2_;
break;
case '/':
{
if(data2_ == 0) exitcode_ = DivZero;
else result_ = data1_ / data2_;
}
break;
case '%':
{
if(data2_ == 0) exitcode_ = ModZero;
else result_ = data1_ % data2_;
} break;
default:
exitcode_ = Unknown;
break;
}
}
void operator ()()
{
run();
}
std::string GetResult()
{
std::string r = std::to_string(data1_);
r += oper_;
r += std::to_string(data2_);
r += "=";
r += std::to_string(result_);
r += "[code: ";
r += std::to_string(exitcode_);
r += "]";
return r;
}
std::string GetTask()
{
std::string r = std::to_string(data1_);
r += oper_;
r += std::to_string(data2_);
r += "=?";
return r;
}
~Task()
{
}
private:
int data1_;
int data2_;
char oper_;
int result_;
int exitcode_;
};
(2)线程池
#pragma once
#include <iostream>
#include<vector>
#include<string>
#include<pthread.h>
#include<queue>
struct ThreadInfo
{
pthread_t tid;
std::string name;
};
static const int deafultnum=5; //默认多少个线程
template <class T>
class ThreadPool
{
public:
void Lock()
{
pthread_mutex_lock(&mutex_);
}
void Unlock()
{
pthread_mutex_unlock(&mutex_);
}
void Wakeup()//线程唤醒
{
pthread_cond_signal(&cond_);
}
void ThreadSleep() //线程休眠
{
pthread_cond_wait(&cond_, &mutex_);
}
bool IsQueueEmpty()
{
return tasks_.empty();
}
std::string GetThreadName(pthread_t tid)
{
for (const auto &ti : threads_)
{
if (ti.tid == tid)
return ti.name;
}
return "None";
}
public:
ThreadPool(int num=deafultnum):threads_(num)
{
pthread_mutex_init(&mutex_,nullptr);
pthread_cond_init(&cond_,nullptr);
}
static void *HandleTask(void *args) //所有线程启动后,就会去检测有没有任务,有任务就执行,没任务就休眠
{
ThreadPool<T> *tp=static_cast<ThreadPool<T>*>(args);
std::string name=tp->GetThreadName(pthread_self());
while (true)
{
tp->Lock();
while(tp->IsQueueEmpty())
{
tp->ThreadSleep();
}
T t=tp->pop();
tp->Unlock();
//当你拿到这个任务,这个任务就是属于你,你不需要在加锁,解锁之间。
t();
std::cout<<name<<"run,"<<"result:"<<t.GetResult()<<std::endl;
}
}
void start()
{
int num=threads_.size();
for(int i=0;i<num;i++)
{
threads_[i].name="thread-"+std::to_string(i+1);
pthread_create(&(threads_[i].tid),nullptr,HandleTask,this);
}
}
T pop()
{
T t=tasks_.front();
tasks_.pop();
return t;
}
void push(const T &t)//往线程池中放任务之后线程才能执行任务
{
Lock();
tasks_.push(t); //有任务,线程别睡了
Wakeup();
Unlock();
}
~ThreadPool()
{
pthread_mutex_destroy(&mutex_);
pthread_cond_destroy(&cond_);
}
private:
std::vector<ThreadInfo> threads_; //这是个vector容器,表示有多少个线程
std::queue<T> tasks_;
pthread_mutex_t mutex_;
pthread_cond_t cond_;
};
(3)主函数
#include <iostream>
#include "ThreadPool.hpp"
#include "Task.hpp"
#include <unistd.h>
int main()
{
ThreadPool<Task> *tp=new ThreadPool<Task>(5);
tp->start();
srand(time(nullptr) ^ getpid());
while(true)
{
//1.构建任务
int x = rand() % 10 + 1;
usleep(10);
int y = rand() % 5;
char op = opers[rand()%opers.size()];
Task t(x, y, op);
tp->push(t);
//ThreadPool<Task>::GetInstance()->Push(t);
//2.交给线程池处理
std::cout << "main thread make task: " << t.GetTask() << std::endl;
sleep(1);
}
}
(4)执行结果