线程池
一个线程池,实际上是由两部分组成的:一个线程安全的队列和一些工作线程,这些工作线程是从队列中获取数据,然后用工作线程去处理。因此,线程池当中的线程是消费线程
请注意:队列中存储的已经不是简单的待处理数据了,而是一个特殊类型,里面存放了两个东西:一个是待处理的数据,另一个是处理这个数据的方法。
线程调用传入进来的函数,处理数据
因为队列中存放类型的特殊性,就可以让线程池能够处理各种各样的数据:
因为队列中既存放了待处理的数据,也存放了处理该数据的函数。
线程池的代码实现
创建一个线程池,代码包含关系是:线程池包含一个线程安全队列和一堆线程,线程安全队列中包含一个队列和一些信号量,队列中每个元素是一个队列元素类型,每个队列元素类型中包含待处理的数据和处理数据的方法
#pragma once
#include<iostream>
#include<pthread.h>
#include<stdio.h>
#include<queue>
#include<semaphore.h>
typedef void(*Handler)(int);
//队列元素类型
class QueueType{
public:
QueueType(int data, Handler handler)
{
_data = data;
_handler = handler;
}
QueueType()
{}
~QueueType()
{}
void Run()
{
_handler(_data);//用传递的函数处理数据
}
private:
int _data;//待要处理的数据
Handler _handler;//处理数据的方法
};
class SafeQueue
{
public:
SafeQueue()
{
sem_init(&_lock, 0, 1);
sem_init(&cons_sem, 0, 0);
sem_init(&prod_sem, 0, 2);//初始只有一个空闲资源
}
void push(QueueType& data)
{
//先保证对于生产者而言,是有资源的,有资源再去抢_lock
//实际上就是要先保证同步
sem_wait(&prod_sem);
//当生产者有资源时,要保证生产者之间是互斥的,所以要抢_lock
//实际上就是要保证互斥
sem_wait(&_lock);
_que.push(data);
//当某一个生产者生产完,需要释放_lock,让其他生产者生产
sem_post(&_lock);
//当生产者生产完,需要通知消费者来消费
sem_post(&cons_sem);
}
void pop(QueueType* data)
{
sem_wait(&cons_sem);
sem_wait(&_lock);
*data = _que.front();
_que.pop();
sem_post(&_lock);
sem_post(&prod_sem);
}
private:
std::queue<QueueType> _que;
/*
* 保证多个线程互斥访问,
* */
sem_t _lock;
//消费者信号量,初始化资源为0,后期资源个数由生产者加
sem_t cons_sem;
//生产者信号量,描述队列的空闲空间,初始化由程序员定,后期资源个数由消费者加
sem_t prod_sem;
};
class ThreadPool
{
public:
ThreadPool(int thread_count = 2)
{
_sq = NULL;
_thread_count = thread_count;
}
~ThreadPool()
{
if(_sq)
{
delete _sq;
_sq = NULL;
}
}
int OnInit(int tc = 2)
{
_thread_count = tc;
_sq = new SafeQueue();
if(_sq == NULL)
{
return -1;
}
return 0;
}
//启动线程池
int OnStart()
{
int fail_count = 0;
for(int i = 0; i < _thread_count; ++i)
{
pthread_t tid;
int ret = pthread_create(&tid, NULL, workerstart, (void*)this);
if(ret < 0)
{
fail_count++;
}
}
_thread_count -= fail_count;//更新成功的线程数量
if(_thread_count <= 0)
{
return -1;
}
return 0;
}
static void* workerstart(void* arg)
{
pthread_detach(pthread_self());
ThreadPool* tp = (ThreadPool*)arg;
while(1)
{
//1.从队列中拿数据
QueueType qt;
tp->_sq->pop(&qt);
//2.进行处理
qt.Run();
}
return NULL;
}
void push(QueueType& qt)
{
_sq->push(qt);
}
private:
SafeQueue* _sq;//线程安全的指针,还未实例化
//一堆线程
int _thread_count;
};
通过主线程创建线程池,并且将主线程作为生产者线程,向线程池的队列中放数据(包含待处理的数据和处理数据的方法)
#include"ThreadPool.hpp"
#include<unistd.h>
void fun(int data)
{
data += 100;
printf("deal data, %d\n", data);
}
int main()
{
//创建线程池
ThreadPool tp;
//初始化线程池
if(tp.OnInit() < 0)
{
return 0;
}
//启动线程池
while(tp.OnStart() < 0)
{
;
}
//往线程池的队列中放入数据
for(int i = 0; i > 10000; ++i)
{
QueueType qt(i, fun);
tp.push(qt);
}
while(1)
{
sleep(1);
}
return 0;
}