线程池原理并用C语言实现

10 篇文章 0 订阅
4 篇文章 0 订阅
  • 线程池的目的是为了减少线程创建、销毁所带来的代价,当有非常多的任务需要独立的线程去做时,可以使用线程池,从线程池中获取线程来处理一个个任务。

        本文就来实现一个线程池。实现环境为Linux操作系统,采用C语言实现pthread线程的线程池。

    线程分配设计:

        线程池中的每个子线程都是等价的。我们用线程信号量来控制子线程和任务的分配问题。设置一个信号量来表示任务队列中的任务资源。每个子线程都会处于死循环中,每轮循环首先等待一个任务资源信号量,当等到之后,互斥地从任务队列中摘取一个任务结点,任务结点中记录着该任务所要执行的函数指针及其参数。之后子线程开始执行该任务。执行完之后释放一个信号量并进入下一轮循环。当没有信号量小于1时,子线程将会阻塞。

        因此一个任务由哪一个线程来执行,这要看哪个线程能够获取到对应的信号量资源。

    具体实现:

        任务队列由双向链表构造,每个节点包含一个任务的函数指针和参数指针。

    一般一个简单的线程池有下列组件:

    1. 线程池管理器(ThreadPoolManager):用于创建并管理线程池
    2. 工作线程(WorkThread): 线程池中线程
    3. 任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行。
    4. 任务队列:用于存放没有处理的任务。提供一种缓冲机制。


    代码如下:

    //threadpool.h

    /**********************************************
      * @author Jacky Lau
      * @date   10/26/2014
      *
      *
      * **********************************************/
    
    #include<pthread.h>
    #include<semaphore.h>
    
    
    /*========================STRUCTS==========================*/
    
    typedef void *(*function_t)(void *arg);
    
    /* jobs */
    typedef struct thread_job_t{
        function_t pf;      /**< function pointer        */
        void *arg;          /**< function's pointer      */
        struct thread_job_t *next; /**< pointer to next job     */
        struct thread_job_t*prev; /**< pointer to previous job */
    }thread_job_t;
    
    
    /* threadpool */
    typedef struct threadpool_t{
        pthread_t *threads; /**< pointer to thread's ID */
        int num_threads;    /**< number of threads      */
    
        thread_job_t *head; /**< pointer to head of workqueue */
        thread_job_t *tail; /**< pointer to tail of workqueue */
        int num_jobs;       /**< number of jobs in the workqueue*/
        sem_t *queue_sem;
    
    }threadpool_t;
    
    /*==========================FUNCTION=================================*/
    
    /*-------------------------threadpool specific------------------------------- */
    
    /**
     * @brief initialize threadpool
     * Allocates memory for the threadpool, workqueue,semphore and fixes pointer
     * in job queue
     * @param num_threads to be used
     * @return pointer to threadpool_t struct on success,
     *         NULL on error
     */
    threadpool_t *threadpool_init(int num_threads);
    
    
    /**
     * @brief destroy threadpool
     * if there are so many jobs and can't handle all of them, free the job_queue
     * if there are so many threads, wake all of them in case of zoombie
     * @param threadpool
     */
    void threadpool_destroy(threadpool_t *threadpool);
    
    /*-------------------------work specific--------------------------------- */
    
    /**
     * @brief do the jobs, all of the threads are blocked when they first created
     * @param threadpool_in
     */
    void threadpool_thread_do(threadpool_t *threadpool_in);
    
    /**
     * @brief add the job into the job_queue, it will wake the blocked threads by sem_post
     * @param threadpool
     * @param pointer to the job function
     * @param argument's of function
     * @return 0 on success,other on failure
     */
    int threadpool_add_work(threadpool_t *threadpool, function_t pf, void *arg);
    
    
    /*-------------------------queue specific--------------------------------- */
    
    /**
     * @brief add job into the queue
     * @param threadpool
     * @param pointer to the struct of thread_job_t
     * @return 0 on success,
     *         other on failure
     */
    int threadpool_job_enqueue(threadpool_t *threadpool, thread_job_t *job);
    
    /**
     * @brief remove the first data unit of queue
     * @param threadpool
     * @return 0 on success
     *         other on failure
     */
    thread_job_t *threadpool_job_dequeue(threadpool_t *threadpool);
    
    /**
     * @brief make the job_queue empty
     * @param threadpool
     * @return 0 on success
     *         other on failure
     */
    int threadpool_job_queue_empty(threadpool_t *threadpool);

    //threadpool.c

    #include"threadpool.h"
    #include<stdio.h>
    #include<malloc.h>
    #include<stdlib.h>
    #include<assert.h>
    
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    typedef int bool;
    #define true  1
    #define false 0
    bool thread_islive = true;
    
    #define CHECK_ERROR(a)                            \
    if((a))                                           \
    {                                                 \
        perror("Error at line\n\t" #a "\nSystem Msg");\
        exit(EXIT_FAILURE);                           \
    }
    
    /*==================FUNCTIONS========================*/
    
    threadpool_t *threadpool_init(int num_threads)
    {
        if(num_threads < 1)
            num_threads =1;
        /* malloc the struct threadpool*/
        threadpool_t *threadpool = (threadpool_t *) malloc(sizeof(threadpool_t));
    
        /*malloc the threads' ID */
        threadpool->threads = (pthread_t *)malloc(sizeof(pthread_t) * num_threads);
        threadpool->num_threads = num_threads;
    
        /*initialize the workqueue*/
        threadpool->head = NULL;
        threadpool->tail = NULL;
        threadpool->num_jobs = 0;
    
        /*initialize the semaphore*/
        threadpool->queue_sem = (sem_t *)malloc(sizeof(sem_t));
        sem_init(threadpool->queue_sem, 0, 0);
    
        /*make the threads in pool*/
        int i;
        for(i = 0; i < num_threads; i++)
        {
            pthread_create(&threadpool->threads[i], NULL, (void *)threadpool_thread_do, (void *)threadpool);
        }
        return threadpool;
    }
    
    /*销毁线程池,等待队列中的任务不会再被执行,但是正在运行的线程会一直
    把任务运行完后再退出*/
    
    void threadpool_destroy(threadpool_t *threadpool)
    {
        thread_islive = false;
        int i;
        /*because create threads more than jobs, the threads will be zoombie*, so wake all of them*/
        for(i = 0; i < threadpool->num_threads; i++)
        {
            CHECK_ERROR(sem_post(threadpool->queue_sem));
        }
        CHECK_ERROR(sem_destroy(threadpool->queue_sem));
    
        /*barrier for synchronization*/
        for(i = 0; i < threadpool->num_threads; i++)
            pthread_join(threadpool->threads[i], NULL);
    
        /*dealloc job queue*/
        /*if the jobs is so many and can't handle them, it will cause mem leak, so empty the queue*/
        threadpool_job_queue_empty(threadpool);
        /*dealloc threads*/
        free(threadpool->threads);
        /*dealloc semphore*/
        free(threadpool->queue_sem);
        /*dealloc threadpool*/
        free(threadpool);
    
    }
    
    int threadpool_job_queue_empty(threadpool_t *threadpool)
    {
        thread_job_t *prev = threadpool->head;
        thread_job_t *p = prev;
        while(threadpool->num_jobs)
        {
            p = p->next;
            free(prev);
            prev = p;
            threadpool->num_jobs--;
        }
        threadpool->head = NULL;
        threadpool->tail = NULL;
        return 0;
    }
    
    void threadpool_thread_do(threadpool_t *threadpool_in)
    {
        assert(threadpool_in);
        threadpool_t *threadpool = threadpool_in;
    
        if(sem_wait(threadpool->queue_sem))
        {
            perror("thread waiting for semaphore");
            exit(EXIT_FAILURE);
        }
    
        while(thread_islive)
        {
            pthread_mutex_lock(&mutex);     /* LOCK */
    
            thread_job_t *job= threadpool_job_dequeue(threadpool);
    
            pthread_mutex_unlock(&mutex);    /* UNLOCK */
    
            if(job)
            {
                function_t pf = job->pf;
                void *arg = job->arg;
                pf(arg);
                free(job);
            }
        }
    }
    
    thread_job_t *threadpool_job_dequeue(threadpool_t *threadpool)
    {
        assert(threadpool);
    
        thread_job_t *job = threadpool->head;
        switch (threadpool->num_jobs) {
        case 0:
            job = NULL;
            break;
        case 1:
            threadpool->head = NULL;
            threadpool->tail = NULL;
            threadpool->num_jobs--;
            break;
        default:
            threadpool->head = job->next;
            job->next = NULL;
            threadpool->head->prev = NULL;
            threadpool->num_jobs--;
            break;
        }
        return job;
    }
    
    int threadpool_add_work(threadpool_t *threadpool, function_t pf, void *arg)
    {
        assert(threadpool);
        thread_job_t *job = (thread_job_t *)malloc(sizeof(thread_job_t));
        job->pf = pf;
        job->arg = arg;
        job->next = NULL;
        job->prev = NULL;
    
        pthread_mutex_lock(&mutex);          /* LOCK */
    
        CHECK_ERROR( threadpool_job_enqueue(threadpool, job) );
    
        pthread_mutex_unlock(&mutex);         /* UNLOCK */
        return 0;
    }
    
    int threadpool_job_enqueue(threadpool_t *threadpool, thread_job_t *job)
    {
        assert(threadpool);
        assert(job);
        switch(threadpool->num_jobs)
        {
        case 0:
            threadpool->head = job;
            threadpool->tail = job;
            threadpool->num_jobs++;
            break;
        default:
            threadpool->tail->next = job;
            job->prev = threadpool->tail;
            threadpool->tail = job;
            threadpool->num_jobs++;
        }
    
        sem_post(threadpool->queue_sem);
        return 0;
    }

    //main.c

    /********************************************
     *@author    JackyLau
     *@date      10/26/2014
     *
     *
     * ******************************************/
    
    #include <stdio.h>
    #include <unistd.h>
    #include"threadpool.h"
    
    void task1()
    {
        printf("thread %d running Task 1\n", (int)pthread_self());
        int i;
        /*for delay*/
        int sum = 0;
        for(i = 0; i < 100000; i++)
        {
            sum += i;
        }
    }
    
    void task2(int id)
    {
        printf("%d thread %d running Task 2\n", id, (int)pthread_self());
        /*for delay*/
        int i;
        int sum = 0;
        for(i = 0; i < 100000; i++)
        {
            sum += i;
        }
    }
    
    int main(void)
    {
        threadpool_t *threadpool = threadpool_init(10);
        int i;
        for(i = 0; i < 1000; i++)
        {
            threadpool_add_work(threadpool, (void *)task1, NULL);
            threadpool_add_work(threadpool, (void *)task2, (void *)i);
        }
    
        sleep(5);/*so they can handle all of the jobs, or just a bit of them*/
        threadpool_destroy(threadpool);
    
        //printf("Hello World!\n");
        return 0;
    }
    
    运行结果:

  • 2
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值