简易线程池的实现
一.简介
本文采用C/C++语言在Linux环境下实现了一个简易的线程池,主要功能为:
- 线程池的创建
- 任务的添加
- 线程池的销毁
- 线程池的管理(尚未完成)
二.文件结构
采用分文件编写的方式,分为四部分
- 头文件
threadpool.h
- 任务列表结构体的创建
- threadpool 结构体创建
- 函数声明
- 函数实现
threadpool.cpp
- 基本上使用c语言
- 实现上述提到的三个功能
InitPool
------------------初始化Worker
--------------------执行函数DestoryThreadPool
---------销毁
- 可视为测试文件
main.cpp
- 提供两个测试案例
test01()
,test()
test01()
用来测试多线程执行的正确性,例如是否有读写冲突等test()
用来检验执行速度,与单线程执行的速度进行比较- 两个测试案例可以在主函数入口处进行调用
makefile
文件
- Linux环境下提供编译方式的脚本
三.代码
1.头文件threadpool.h
#ifndef _THREADPOOL_H
#define _THREADPOOL_H
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <chrono>
#include <semaphore.h>
using namespace std;
/*defined the Task */
struct Task
{
void (*function)(void *arg);
void *arg;
};
struct threadpool
{
/*member of Task queue*/
Task *taskQ; // Q refers to queue struct
int Qsize; // set the maxsize of queue
int back; // back of queue to insert
int front; // front of queue to delete
int currentQsize; // current Task in queue
/*member of thread ID*/
pthread_t *workPid; // To launch worker process to deal with Task
pthread_t ManagerPid; // To launch manager process
int maxWorkNumber; // Set the max number of workers
int minWorkNumber; // Set the min number of workers
int currentWorker; // Set the Current Worker
int busyWorkerNumber; // current thread in this program
/*To avoid Thread conflict*/
pthread_mutex_t outStreamMutex; // use it when output strs
pthread_mutex_t poolMutex; // use it when read or write thread pool
sem_t s_TaskEmpty;// use when the TaskQ is full
sem_t s_TaskFull; // use when the TaskQ is full
/*end the pool*/
bool endthepool; // use it when you want to end the pool
};
/*Initial the pool*/
threadpool *InitPool(int max, int min, int Qmax);
/*To deal the Task in Taskqueue*/
void *Worker(void*arg);
/*manager for workers*/
void *Manager(void*arg);
/*Add Tack*/
void *AddTask(threadpool *pool,void (*func)(void *Args),void *arg);
/*free all the mem ,locks,conds, After finish all the tasks*/
void DestoryThreadPool(threadpool *pool);
#endif
- 函数实现
threadpool.cpp
#include "threadPool.h"
static threadpool *pool = NULL;
int arr[1000];
/*Validation of Successful Acquisition */
void reciveNumber(void *arg)
{ /*Try to recive Numbers for 0-999 */
int index;
pthread_mutex_lock(&pool->outStreamMutex);
sscanf((char *)arg, "%d", &index);
arr[index]++;
pthread_mutex_unlock(&pool->outStreamMutex);
}
/*A busy-waiting program*/
void DontSayAnything(void *arg)
{
for (int i = 0; i < 1000000; i++)
{
};
}
/*excute function with pthread*/
void run_with_pthread(int logcialPthread)
{
/*Initial threadpool*/
static threadpool *pool = InitPool(30, logcialPthread, 5);
for (int i = 0; i < 5000; i++)
{
AddTask(pool, DontSayAnything, NULL);
}
DestoryThreadPool(pool);
}
/*excute function without pthread*/
void run_without_pthread()
{
for (int i = 0; i < 5000; i++)
{
DontSayAnything(NULL);
}
}
/*Comparative Testing : run_without_pthread && run_with_pthread*/
void test()
{
int logcialPthread;
cout << "Set the logical pthread : ";
cin >> logcialPthread;
auto beforeTime = chrono::steady_clock::now();
run_with_pthread(logcialPthread);
auto afterTime = chrono::steady_clock::now();
double duration_second = chrono::duration<double>(afterTime - beforeTime).count();
cout << "____The executing time_____" << endl;
cout << " Pthread : " << duration_second << "s" << endl;
beforeTime = chrono::steady_clock::now();
run_without_pthread();
afterTime = chrono::steady_clock::now();
duration_second = chrono::duration<double>(afterTime - beforeTime).count();
cout << "No pthread : " << duration_second << "s" << endl;
}
void test01()
{
/*Initial rec array*/
for (int i = 0; i < 1000; i++)
{
arr[i] = 0;
}
pool = InitPool(30, 20, 10);
char **strs = new char *[1000];
for (int i = 0; i < 1000; i++)
{
strs[i] = new char[10];
sprintf(strs[i], "%d", i);
AddTask(pool, reciveNumber, strs[i]);
}
DestoryThreadPool(pool);
/*Give conclusion*/
for (int i = 0; i < 1000; i++)
{
if (arr[i] != 1)
{
cout << "The ans is not right." << endl;
break;
}
}
cout << "Get ALL the number for 0 to 999" << endl;
}
int main()
{
test();
// test01();
}
- 函数实现
#include "threadPool.h"
/*create a threadpool*/
threadpool *InitPool(int max, int min, int Qmax)
{
/*allocate memory*/
threadpool *pool = new threadpool;
pool->taskQ = new Task[Qmax];
pool->workPid = new pthread_t[max];
do
{
/* check the mem*/
if (pool->workPid == NULL || pool == NULL || pool->taskQ == NULL)
{
cout << "Failed to allocate memory " << endl;
break;
}
/*Initial static value*/
pool->Qsize = Qmax;
pool->back = 0;
pool->front = 0;
pool->maxWorkNumber = max;
pool->currentWorker = min;
pool->minWorkNumber = min;
pool->busyWorkerNumber = 0;
pool->endthepool = false;
pool->currentQsize = 0;
/*Initial mutex and sem*/
pthread_mutex_init(&pool->poolMutex, NULL);
pthread_mutex_init(&pool->outStreamMutex, NULL);
sem_init(&pool->s_TaskEmpty, 0, pool->Qsize);
sem_init(&pool->s_TaskFull, 0, 0);
/*launch Manger*/
if (pthread_create(&pool->ManagerPid, NULL, Manager, (void *)pool) != 0)
{
cout << "Failed to launch Manager " << endl;
break;
}
/*launch Worker with min size*/
for (int i = 0; i < min; i++)
{
if (pthread_create(&pool->workPid[i], NULL, Worker, (void *)pool))
{
cout << "Failed to launch Worker " << endl;
break;
}
}
return pool;
} while (0);
/*delete mem if failed to Initial*/
if (pool && pool->taskQ)
delete[] pool->taskQ;
if (pool && pool->workPid)
delete[] pool->workPid;
if (pool)
delete pool;
return NULL;
}
/*Get Task And Execute*/
void *Worker(void *arg)
{
Task task;
threadpool *pool = (threadpool *)arg; //translate Parameters
while (1)
{
if (sem_trywait(&pool->s_TaskFull) == 0)// wait for Produce
{
/*get Task for queue*/
pthread_mutex_lock(&pool->poolMutex); /*lock the pool*/
task.function = pool->taskQ[pool->front].function; /*get function*/
task.arg = pool->taskQ[pool->front].arg; /*get args*/
pool->front = (pool->front + 1) % pool->Qsize; /*update the Task queue*/
pool->currentQsize--; /*reduce the number in Task queue*/
pool->busyWorkerNumber++; /*means the increase of pthread*/
pthread_mutex_unlock(&pool->poolMutex); /*unlock the pool*/
sem_post(&pool->s_TaskEmpty);
/*Execute Task */
task.function(task.arg);
/*update*/
pthread_mutex_lock(&pool->poolMutex);
pool->busyWorkerNumber--;
pthread_mutex_unlock(&pool->poolMutex);
}
else
{
pthread_mutex_lock(&pool->poolMutex);
/*Check whether the thread should be terminated. */
if (pool->currentQsize == 0 && pool->endthepool == true)
{
pthread_mutex_unlock(&pool->poolMutex);
pthread_exit(NULL);
}
pthread_mutex_unlock(&pool->poolMutex);
}
}
}
/*Add Task*/
void *AddTask(threadpool *pool, void (*func)(void *Args), void *arg)
{
/*similar to a producer*/
sem_wait(&pool->s_TaskEmpty);
pthread_mutex_lock(&pool->poolMutex);
pool->taskQ[pool->back].function = func;
pool->taskQ[pool->back].arg = arg;
pool->back = (pool->back + 1) % pool->Qsize; /*update*/
pool->currentQsize++;
pthread_mutex_unlock(&pool->poolMutex);
sem_post(&pool->s_TaskFull);
return NULL;
}
/*free all the mem ,locks,conds, After finish all the tasks*/
void DestoryThreadPool(threadpool *pool)
{
/*Set the end flag*/
pthread_mutex_lock(&pool->poolMutex);
pool->endthepool = true;
pthread_mutex_unlock(&pool->poolMutex);
/*ensure finishing all the task*/
for (int i = 0; i < pool->currentWorker; i++)
{
pthread_join(pool->workPid[i], NULL);
}
pthread_join(pool->ManagerPid, NULL);
/*destroy mutexs and cons*/
sem_destroy(&pool->s_TaskEmpty);
sem_destroy(&pool->s_TaskFull);
pthread_mutex_destroy(&pool->poolMutex);
pthread_mutex_destroy(&pool->outStreamMutex);
/*free mem*/
delete[] pool->workPid;
delete[] pool->taskQ;
delete pool;
pool = NULL;
}
/*To increase or reduce the number of pthread*/
void *Manager(void *arg)
{
/*not finish*/
threadpool *pool = (threadpool *)arg;
return NULL;
}
- Makefile文件
ALL = Server Client Pthread
clear :
@rm -rf *.out *.s *.o test.cpp $(ALL)
threadPool :
@g++ -g threadPool.cpp main.cpp -o threadPool
@./threadPool
@rm threadPool
.PHONY : clear threadPool
四.执行结果
test()
zylian@zylian:~/code/Web$ make threadPool
Set the logical pthread : 14
____The executing time_____
Pthread : 0.688714s
No pthread : 7.89239s
本机采用的逻辑线程为14,以此设置参数,可发现多线程执行比单线程速度快了11.4倍,考虑到进程切换等开销,认为该数据可以接受
test01()
正确性检验,主要是用于检验是否有读写冲突,以及是否可以执行任务列表中全部任务
zylian@zylian:~/code/Web$ make threadPool
Get ALL the number for 0 to 999
5. 杂记
-
需要注意的点是
set_trywait()
的使用,当程序执行销毁后,并且任务列表中无任务时,不可以让程序陷入等待信号量的状态,因为此时没有生产者产生任务到缓冲区,此时必须使用set_trywait()
判定执行状态,是否应该退出线程。 -
本人代码能力有限(水平较low),在找到以上bug的时候耗费了很多时间。
-
代码中也有未完善之处,有时间再来弥补。
-
附上邮箱 欢迎讨论 zylian@mail.ustc.edu.cn