池化思想:线程池,字符串常量池,数据库连接池
提高资源的利用率
说明:
- 当来了任务时sever将任务指派给线程池,由线程完成任务工作
- sever是从网络中读取任务,而线程池负责消化任务,两组线程就需要任务对列将他门连接起来
- sever负责将任务塞到任务队列中,将指派给特定的线程
一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着
监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。
#include<iostream>
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>
#include<queue>
using namespace std;
class test {
private:
int base;
public:
//bool isempty(){
//return q.size()==0;
//}
test() {}
test(int a) :base(a) {}
void run() {
cout << "test is running。。" << base << "线程id:" << pthread_self() << endl;
}
~test() {}
};
class ThreadPool {
private:
queue<test*> q;
int cap;//线程池数量
pthread_mutex_t lock;
pthread_cond_t cond;
bool quit;//线程突出
public:
bool isempty() {
return q.size() == 0;
}
ThreadPool(int cap_) :cap(cap_), quit(false)
{}
void Init() {
pthread_mutex_init(&lock, NULL);
pthread_cond_init(&cond, NULL);
pthread_t t;
for (int i = 0; i < cap; i++) {
pthread_create(&t, NULL, thread_running, (void*)this);
}
}
void Lock() {
pthread_mutex_lock(&lock);
}
void UnLock() {
pthread_mutex_unlock(&lock);
}
void Waitcond() {
pthread_cond_wait(&cond, &lock);//若没有直接阻塞自己
}
void threadswakeup() {
pthread_cond_broadcast(&cond);//唤醒所有的线程
}
bool Quit() {
return quit;
}
static void* thread_running(void* rid) {
ThreadPool* this_d = (ThreadPool*)rid;
while (!this_d->Quit()) {//检测是否达到线程退出条件
//pthread_mutex_lock(&lock);
this_d->Lock();
while (!this_d->Quit() && this_d->isempty()) {//检测有没有任务来
//这里检测quit因为在线程退出后还会唤醒线程,要保证线程不退出才能接到任务
//pthread_cond_wait(&cond);//若没有直接阻塞自己
this_d->Waitcond();
}
test t;
if (!this_d->Quit() && !this_d->isempty()) {
this_d->out(t);//提取出任务
}//只有保证线程不退出才能拿任务
t.run();//执行任务在任务类中执行
this_d->UnLock();
//pthread_mutex_unlock(&lock);
}
}
void put(test& t) {
pthread_mutex_lock(&lock);
q.push(&t);
pthread_mutex_unlock(&lock);
pthread_cond_signal(&cond);;
}
void threadquit() {
if (!isempty()) {//任务队列中有任务不能线程不能退出
return;
}
quit = true;//线程退出条件
threadswakeup();
}
void out(test& t) {
test* a = q.front();
t = *a;
q.pop();
}
~ThreadPool() {
pthread_mutex_destroy(&lock);
pthread_cond_destroy(&cond);
}
};
int main() {
ThreadPool* td = new ThreadPool(10);
td->Init();
while (1) {
int s = rand() % 10 + 1;
test t(s);
td->put(t);
sleep(1);
}
return 0;
}
说明:
为什么定义任务队列要定义成这样queue<test*> q;?
因为在任务复制时一定回调test的默认拷贝函数,或者赋值语句不管用什么方式都是要把任务整体拷贝一份。当任务量很大时,效率就成了问题,所以这里用指针效率就是恒定的。
当任务队列没任务时要让特定线程等(死循环,直到检测到任务队列不为空为止),直到有任务来,接着唤醒线程池线程来执行任务
为什么在函数thread_running()前要加static?
因为当thread_running当作成员函数,而内部成员函数在编译时都默认加一个this指针作为第一个参数,一旦this指针做第一个参数这个函数就成了两个参数第一个this指针,第二个void*因为当创建线程时int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(start_routine)(void), void arg);第三个参数回调函数void (start_routine)(void),只有一个参数void,当thread_running时成员函数时会传入两个参数,所以在该函数要加static,表明该函数属于该类 不具有this指针,static函数无法放问成员函数,也无法找到某一个对象的成员函数,所以在创建线程时要将this指针传进来。
pthread_create(&t, NULL, thread_running, (void)this);
十个线程组成的线程池依次执行任务。
什么会按顺序指派给线程?线程实在等待cond排的对 为什么一次之唤醒线程?当一次唤醒所有线程,首先执行次序会不固定,接着一次唤醒所有线程会造成惊群效应
惊群效应请参考这里
线程池存在的价值:
1 有任务,立马有线程进行服务,省掉了线程创建
2 有效防止sever中线程过多,导致系统过载的问题