基于Linux的简易线程池实现

简易线程池的实现

一.简介

本文采用C/C++语言在Linux环境下实现了一个简易的线程池,主要功能为:

  • 线程池的创建
  • 任务的添加
  • 线程池的销毁
  • 线程池的管理(尚未完成)

二.文件结构

采用分文件编写的方式,分为四部分

  1. 头文件 threadpool.h
  • 任务列表结构体的创建
  • threadpool 结构体创建
  • 函数声明
  1. 函数实现 threadpool.cpp
  • 基本上使用c语言
  • 实现上述提到的三个功能
  • InitPool------------------初始化
  • Worker--------------------执行函数
  • DestoryThreadPool---------销毁
  1. 可视为测试文件 main.cpp
  • 提供两个测试案例 test01()test()
  • test01() 用来测试多线程执行的正确性,例如是否有读写冲突等
  • test() 用来检验执行速度,与单线程执行的速度进行比较
  • 两个测试案例可以在主函数入口处进行调用
  1. 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
  1. 函数实现 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();
}
  1. 函数实现
#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;
}
  1. 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

四.执行结果

  1. 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倍,考虑到进程切换等开销,认为该数据可以接受

  1. 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

  • 10
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值