在多线程的操作中,我们经常使用线程池来进行大量的计算、缓存磁盘、写log等。线程池主要由 任务队列、 执行队列和 管理组件。三部分组成。如图,中间的框框就是我们的线程池,threadqueue负责从taskqueue取任务,当没有任务时,thread通过条件变量进行等待。处理完成之后,返回处理的结果。
任务队列
typedef struct job {
void (*func)(struct job *arg);
void *userdata;
struct job *prev;
struct job *next;
}job_t;
任务队列主要主要有回调处理函数,和用户数据、前指针和后指针组成。
执行队列
typedef struct work {
pthread_t thread;
int treminate;
struct workqueue *threadpool;
struct work *prev;
struct work *next;
}work_t;
执行队列就是我们的threadqueue。 主要有线程id, terminate标志可以使线程退出,线程池的指针、前指针和后指针组成。
管理组件
typedef struct workqueue{
struct work *works;
struct job *jobs;
pthread_cond_t jobs_cond;
pthread_mutex_t jobs_mutex;
}threadpool;
管理组件就是我们的线程池了,也就是对任务队列和执行队列进行加锁,通知等。
基本组件了解之后,我们就要对线程池进行创建、任务分配、销毁等。
创建线程池
int ThreadPoolCreate(threadpool *pool, int numWorks) {
if(numWorks < 1) numWorks = 1;
memset(pool, 0, sizeof(threadpool));
pthread_cond_t blank_cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t bland_mutex = PTHREAD_MUTEX_INITIALIZER;
memcpy(&pool->jobs_cond, &blank_cond, sizeof(pool->jobs_cond));
memcpy(&pool->jobs_mutex, &bland_mutex, sizeof(pool->jobs_mutex));
for (int i = 0; i < numWorks; ++i) {
work_t *worker = (work_t *)calloc(1, sizeof(work_t));
if(worker == NULL) {
perror("calloc");
return -1;
}
worker->threadpool = pool;
int ret = pthread_create(&worker->thread, NULL, ThreadFunc, (void *)worker);
if(ret) {
perror("pthread_create");
free(worker);
return -1;
}
LL_ADD(worker, worker->threadpool->works);
}
return 0;
}
线程池的创建也很简单,主要对条件变量和互斥锁进行初始化。对每个任务进行创建线程,并加入到threadqueue中。创建完线程之后,线程就开始运行了。
线程运行
static void *ThreadFunc(void *arg) {
work_t *worker = (work_t*)arg;
printf("pid :%u, tid: %ld, self: %lu\n", getpid(), (long int)syscall(__NR_gettid), pthread_self());
while(1) {
pthread_mutex_lock(&worker->threadpool->jobs_mutex);
while(worker->threadpool->jobs == NULL) {
if(worker->treminate) break;
pthread_cond_wait(&worker->threadpool->jobs_cond, &worker->threadpool->jobs_mutex);
}
if(worker->treminate) break;
job_t *jober = worker->threadpool->jobs;
if(jober != NULL) {
LL_REMOVE(jober, worker->threadpool->jobs);
}
pthread_mutex_unlock(&worker->threadpool->jobs_mutex);
if(jober == NULL) continue;
jober->func(jober);
}
free(worker);
pthread_exit(NULL);
}
在线程里面,我们主要就是取任务,如果没有任务,我们就等待,如果有设置terminate, 我们就退出。取到了任务之后,我们就执行任务队列的回调函数。
任务回调函数
static void client(job_t *arg) {
int index = *(int*)arg->userdata;
printf("index: %d, selfid: %lu\n", index, pthread_self());
free(arg->userdata);
free(arg);
}
我们的任务处理函数很简单,主要对传进来的参数进行读取。
整个code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/syscall.h>
#define LL_ADD(item, list) do { \
item->prev = NULL; \
item->next = list; \
list = item; \
}while(0)
#define LL_REMOVE(item, list) do { \
if(item->prev != NULL) item->prev->next = item->next; \
if(item->next != NULL) item->next->prev = item->prev; \
if(item == list) list = item->next; \
item->prev = item->next = NULL; \
}while(0)
typedef struct work {
pthread_t thread;
int treminate;
struct workqueue *threadpool;
struct work *prev;
struct work *next;
}work_t;
typedef struct job {
void (*func)(struct job *arg);
void *userdata;
struct job *prev;
struct job *next;
}job_t;
typedef struct workqueue{
struct work *works;
struct job *jobs;
pthread_cond_t jobs_cond;
pthread_mutex_t jobs_mutex;
}threadpool;
static void *ThreadFunc(void *arg) {
work_t *worker = (work_t*)arg;
printf("pid :%u, tid: %ld, self: %lu\n", getpid(), (long int)syscall(__NR_gettid), pthread_self());
while(1) {
pthread_mutex_lock(&worker->threadpool->jobs_mutex);
while(worker->threadpool->jobs == NULL) {
if(worker->treminate) break;
pthread_cond_wait(&worker->threadpool->jobs_cond, &worker->threadpool->jobs_mutex);
}
if(worker->treminate) break;
job_t *jober = worker->threadpool->jobs;
if(jober != NULL) {
LL_REMOVE(jober, worker->threadpool->jobs);
}
pthread_mutex_unlock(&worker->threadpool->jobs_mutex);
if(jober == NULL) continue;
jober->func(jober);
}
free(worker);
pthread_exit(NULL);
}
int ThreadPoolCreate(threadpool *pool, int numWorks) {
if(numWorks < 1) numWorks = 1;
memset(pool, 0, sizeof(threadpool));
pthread_cond_t blank_cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t bland_mutex = PTHREAD_MUTEX_INITIALIZER;
memcpy(&pool->jobs_cond, &blank_cond, sizeof(pool->jobs_cond));
memcpy(&pool->jobs_mutex, &bland_mutex, sizeof(pool->jobs_mutex));
for (int i = 0; i < numWorks; ++i) {
work_t *worker = (work_t *)calloc(1, sizeof(work_t));
if(worker == NULL) {
perror("calloc");
return -1;
}
worker->threadpool = pool;
int ret = pthread_create(&worker->thread, NULL, ThreadFunc, (void *)worker);
if(ret) {
perror("pthread_create");
free(worker);
return -1;
}
LL_ADD(worker, worker->threadpool->works);
}
return 0;
}
void ThreadPoolDestory(threadpool *pool){
work_t *worker = NULL;
for(worker = pool->works; worker != NULL; worker = worker->next) {
worker->treminate = 1;
}
pthread_mutex_lock(&pool->jobs_mutex);
pool->works = NULL;
pool->jobs = NULL;
pthread_cond_broadcast(&pool->jobs_cond);
pthread_mutex_unlock(&pool->jobs_mutex);
}
static void client(job_t *arg) {
int index = *(int*)arg->userdata;
printf("index: %d, selfid: %lu\n", index, pthread_self());
free(arg->userdata);
free(arg);
}
void ThreadPoolQueue(threadpool *pool, job_t *job) {
pthread_mutex_lock(&pool->jobs_mutex);
LL_ADD(job, pool->jobs);
pthread_cond_signal(&pool->jobs_cond);
pthread_mutex_unlock(&pool->jobs_mutex);
}
int main(int argc, char **argv) {
threadpool pool;
if(argc < 1) {
printf("usage:%d\n", argc);
return -1;
}
int numworks = atoi(argv[1]);
ThreadPoolCreate(&pool, numworks);
for(int i = 0; i < numworks; ++i) {
job_t *jober = (job_t*)calloc(1, sizeof(job_t));
if(jober == NULL) {
perror("calloc");
return -1;
}
jober->func = client;
jober->userdata = calloc(1, sizeof(int));
*(int *)jober->userdata = i;
ThreadPoolQueue(&pool, jober);
}
getchar();
printf("\n");
//ThreadPoolDestory(&pool->thread);
return 0;
}
code2:
threadpool.h
#include <pthread.h>
struct job
{
void* (*callback_function)(void *arg); //线程回调函数
void *arg; //回调函数参数
struct job *next;
};
struct threadpool
{
int thread_num; //线程池中开启线程的个数
int queue_max_num; //队列中最大job的个数
struct job *head; //指向job的头指针
struct job *tail; //指向job的尾指针
pthread_t *pthreads; //线程池中所有线程的pthread_t
pthread_mutex_t mutex; //互斥信号量
pthread_cond_t queue_empty; //队列为空的条件变量
pthread_cond_t queue_not_empty; //队列不为空的条件变量
pthread_cond_t queue_not_full; //队列不为满的条件变量
int queue_cur_num; //队列当前的job个数
int queue_close; //队列是否已经关闭
int pool_close; //线程池是否已经关闭
};
//================================================================================================
//函数名: threadpool_init
//函数描述: 初始化线程池
//输入: [in] thread_num 线程池开启的线程个数
// [in] queue_max_num 队列的最大job个数
//输出: 无
//返回: 成功:线程池地址 失败:NULL
//================================================================================================
struct threadpool* threadpool_init(int thread_num, int queue_max_num);
//================================================================================================
//函数名: threadpool_add_job
//函数描述: 向线程池中添加任务
//输入: [in] pool 线程池地址
// [in] callback_function 回调函数
// [in] arg 回调函数参数
//输出: 无
//返回: 成功:0 失败:-1
//================================================================================================
int threadpool_add_job(struct threadpool *pool, void* (*callback_function)(void *arg), void *arg);
//================================================================================================
//函数名: threadpool_destroy
//函数描述: 销毁线程池
//输入: [in] pool 线程池地址
//输出: 无
//返回: 成功:0 失败:-1
//================================================================================================
int threadpool_destroy(struct threadpool *pool);
//================================================================================================
//函数名: threadpool_function
//函数描述: 线程池中线程函数
//输入: [in] arg 线程池地址
//输出: 无
//返回: 无
//================================================================================================
void* threadpool_function(void* arg);
threadpool.cpp
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <sys/time.h>
#include <unistd.h>
#include <iostream>
#include <sstream>
#include "threadpool.h"
using namespace std;
#define TASK_NUMBER 100
struct threadpool *threadpool_init(int thread_num, int queue_max_num)
{
struct threadpool *pool = NULL;
do
{
pool = (struct threadpool *)malloc(sizeof(struct threadpool));
if (NULL == pool)
{
printf("failed to malloc threadpool!\n");
break;
}
pool->thread_num = thread_num;
pool->queue_max_num = queue_max_num;
pool->queue_cur_num = 0;
pool->head = NULL;
pool->tail = NULL;
if (pthread_mutex_init(&(pool->mutex), NULL))
{
printf("failed to init mutex!\n");
break;
}
if (pthread_cond_init(&(pool->queue_empty), NULL))
{
printf("failed to init queue_empty!\n");
break;
}
if (pthread_cond_init(&(pool->queue_not_empty), NULL))
{
printf("failed to init queue_not_empty!\n");
break;
}
if (pthread_cond_init(&(pool->queue_not_full), NULL))
{
printf("failed to init queue_not_full!\n");
break;
}
pool->pthreads = (pthread_t *)malloc(sizeof(pthread_t) * thread_num);
if (NULL == pool->pthreads)
{
printf("failed to malloc pthreads!\n");
break;
}
pool->queue_close = 0;
pool->pool_close = 0;
int i;
for (i = 0; i < pool->thread_num; ++i)
{
pthread_create(&(pool->pthreads[i]), NULL, threadpool_function, (void *)pool);
}
return pool;
} while (0);
return NULL;
}
int threadpool_add_job(struct threadpool *pool, void *(*callback_function)(void *arg), void *arg)
{
assert(pool != NULL);
assert(callback_function != NULL);
assert(arg != NULL);
pthread_mutex_lock(&(pool->mutex));
while ((pool->queue_cur_num == pool->queue_max_num) && !(pool->queue_close || pool->pool_close))
{
pthread_cond_wait(&(pool->queue_not_full), &(pool->mutex)); //队列满的时候就等待
}
if (pool->queue_close || pool->pool_close) //队列关闭或者线程池关闭就退出
{
pthread_mutex_unlock(&(pool->mutex));
return -1;
}
struct job *pjob = (struct job *)malloc(sizeof(struct job));
if (NULL == pjob)
{
pthread_mutex_unlock(&(pool->mutex));
return -1;
}
pjob->callback_function = callback_function;
pjob->arg = arg;
pjob->next = NULL;
if (pool->head == NULL)
{
pool->head = pool->tail = pjob;
pthread_cond_broadcast(&(pool->queue_not_empty)); //队列空的时候,有任务来时就通知线程池中的线程:队列非空
}
else
{
pool->tail->next = pjob;
pool->tail = pjob;
}
pool->queue_cur_num++;
pthread_mutex_unlock(&(pool->mutex));
return 0;
}
static uint64_t get_tick_count()
{
struct timeval tval;
uint64_t ret_tick;
gettimeofday(&tval, NULL);
ret_tick = tval.tv_sec * 1000L + tval.tv_usec / 1000L;
return ret_tick;
}
void *threadpool_function(void *arg)
{
struct threadpool *pool = (struct threadpool *)arg;
struct job *pjob = NULL;
uint64_t start_time = get_tick_count();
uint64_t end_time = get_tick_count();
while (1) //死循环
{
pthread_mutex_lock(&(pool->mutex));
while ((pool->queue_cur_num == 0) && !pool->pool_close) //队列为空时,就等待队列非空
{
end_time = get_tick_count(); // 没有任务的时候设置读取最后处理任务的时间
printf("threadpool need time:%lums\n", end_time - start_time);
pthread_cond_wait(&(pool->queue_not_empty), &(pool->mutex));
}
if (pool->pool_close) //线程池关闭,线程就退出
{
pthread_mutex_unlock(&(pool->mutex));
pthread_exit(NULL);
}
pool->queue_cur_num--;
pjob = pool->head;
if (pool->queue_cur_num == 0)
{
pool->head = pool->tail = NULL;
}
else
{
pool->head = pjob->next;
}
if (pool->queue_cur_num == 0)
{
pthread_cond_signal(&(pool->queue_empty)); //队列为空,就可以通知threadpool_destroy函数,销毁线程函数
}
if (pool->queue_cur_num == pool->queue_max_num - 1)
{
pthread_cond_broadcast(&(pool->queue_not_full)); //队列非满,就可以通知threadpool_add_job函数,添加新任务
}
pthread_mutex_unlock(&(pool->mutex));
(*(pjob->callback_function))(pjob->arg); //线程真正要做的工作,回调函数的调用
free(pjob);
pjob = NULL;
}
}
int threadpool_destroy(struct threadpool *pool)
{
assert(pool != NULL);
pthread_mutex_lock(&(pool->mutex));
if (pool->queue_close || pool->pool_close) //线程池已经退出了,就直接返回
{
pthread_mutex_unlock(&(pool->mutex));
return -1;
}
pool->queue_close = 1; //置队列关闭标志
while (pool->queue_cur_num != 0)
{
pthread_cond_wait(&(pool->queue_empty), &(pool->mutex)); //等待队列为空
}
pool->pool_close = 1; //置线程池关闭标志
pthread_mutex_unlock(&(pool->mutex));
pthread_cond_broadcast(&(pool->queue_not_empty)); //唤醒线程池中正在阻塞的线程
pthread_cond_broadcast(&(pool->queue_not_full)); //唤醒添加任务的threadpool_add_job函数
int i;
for (i = 0; i < pool->thread_num; ++i)
{
pthread_join(pool->pthreads[i], NULL); //等待线程池的所有线程执行完毕
}
pthread_mutex_destroy(&(pool->mutex)); //清理资源
pthread_cond_destroy(&(pool->queue_empty));
pthread_cond_destroy(&(pool->queue_not_empty));
pthread_cond_destroy(&(pool->queue_not_full));
free(pool->pthreads);
struct job *p;
while (pool->head != NULL)
{
p = pool->head;
pool->head = p->next;
free(p);
}
free(pool);
return 0;
}
// #define random(x) (rand()%x)
static string int2string(uint32_t user_id)
{
stringstream ss;
ss << user_id;
return ss.str();
}
int main(int argc, char *argv[])
{
thread_num = atoi(argv[1]);
printf("thread_num = %d,\n", thread_num);
struct threadpool *pool = threadpool_init(thread_num, TASK_NUMBER); // 初始化线程
for (int i = 0; i < TASK_NUMBER; i++)
{
if (use_pool) // 使用连接池时任务采用workUsePool
{
threadpool_add_job(pool, work, (void *)pDBPool);
}
else // 不使用连接池时任务采用workNoPool
{
threadpool_add_job(pool, work, (void *)pDBPool);
}
}
while(pool->queue_cur_num !=0 ) // 判断队列是否还有任务
{
sleep(1); // 还有任务主线程继续休眠
}
sleep(2); // 没有任务要处理了休眠2秒退出,这里等待主要是确保所有线程都已经空闲
threadpool_destroy(pool);
delete pDBPool;
return 0;
}