【池化技术】线程池技术原理和C语言实现_线程池的工作原理c语言(1)

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

多进程和多线程区别

多进程:操作系统中同时运行的多个程序

多线程:在同一个进程中同时运行的多个任务

举个例子,多线程下载软件,可以同时运行多个线程,但是通过程序运行的结果发现,每一次结果都不一致。 因为多线程存在一个特性:随机性。造成的原因:CPU在瞬间不断切换去处理各个线程而导致的,可以理解成多个线程在抢CPU资源。

多线程提高CPU使用率

多线程

多线程并不能提高运行速度,但可以提高运行效率,让CPU的使用率更高。但是如果多线程有安全问题或出现频繁的上下文切换时,运算速度可能反而更低。

二、线程池技术

线程池(thread pool)技术是指能够保证所创建的任一线程都处于繁忙状态,而不需要频繁地为了某一任务而创建和销毁线程,因为系统在创建和销毁线程时所耗费的cpu资源很大。如果任务很多,频率很高,为了单一一个任务而起线程而后销线程,那么这种情况效率相当低下的。线程池技术就是用于解决这样一种应用场景而应运而生的。

什么时候需要创建线程池呢?简单的说,如果一个应用需要频繁的创建和销毁线程,而任务执行的时间又非常短,这样线程创建和销毁的带来的开销就不容忽视,这时也是线程池该出场的机会了。如果线程创建T1和销毁时间T3相比任务执行时间T2可以忽略不计,则没有必要使用线程池了。反之如果T1+T3>T2,那就很有必要使用线程池。

线程池技术的工作原理:在起先就创建一定数量的线程以队列形式存在,并为其分配一个工作队列,当工作队列为空时,表示没有任务,此时所有线程挂起等待新的工作到来。当新的工作到来时,线程队列头开始执行这个任务,然后依次是第二、第三个线程执行新到来的任务,当其中某个线程处理完任务后,那么该线程立马开始接受任务分派,从而让所有线程都处于忙碌的状态,提高并行处理效率。

线程池技术是一种典型的生产者-消费者模型。因此,无论用哪种语言实现,只要遵循其原理本身就能够很好的工作了。那么实现线程池技术我们需要考虑到哪些技术性的问题?

C语言线程池技术的实现:

需要考虑的技术问题一,线程池应该包含哪些成员变量。

既然要开一定数量的线程,那么这个“一定数量(max_thread_num)”必定是线程池的一个成员。

如何表示一个线程池是否已经关闭?如果关闭那么必需要立马释放资源。所以“是否关闭(shutdown)”也是一个成员。

创建线程需要有id,必需要为每个线程准备一个id,所以需要一个id数组,其长度就是max_thread_num。

线程锁,用以保证对线程操作时的互斥性。所以需要一个锁,queue_lock。

条件变量(condition_variable),这里使用条件变量主要是为了广播任务到来的消息给所有线程。当有处于空闲的线程,则由此线程

接受任务分派。所以需要一个条件变量queue_ready。

最为重要的就是任务本身,也就是工作。那么工作本身又需要哪几个成员变量?首先肯定是任务入口,routine函数

其次是routine函数的参数args;再次任务是以队列存在着的,所以任务本身应该包含一个next。

需要考虑的技术问题二,线程池应该包含哪些api。

一、创建线程池,create_tpool

二、销毁线程池,destroy_tpool

三、分派任务,add_task_2_tpool

基于上述分析,我们可以先构造头文件。

tpool.h

#ifndef T\_POOL
#define T\_POOL
 
#include <pthread.h>
#include <ctype.h>
 
typedef struct tpool\_work{
   void\* (\*work_routine)(void\*); //function to be called
   void\* args;                   //arguments 
   struct tool\_work\* next;
}tpool\_work\_t;
 
typedef struct tpool{
   size\_t               shutdown;       //is tpool shutdown or not, 1 ---> yes; 0 ---> no
   size\_t               maxnum_thread; // maximum of threads
   pthread\_t            \*thread_id;     // a array of threads
   tpool\_work\_t\*        tpool_head;     // tpool\_work queue
   pthread\_cond\_t       queue_ready;    // condition varaible
   pthread\_mutex\_t      queue_lock;     // queue lock
}tpool\_t;
 
 
/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
\*@brief:
\* create thread pool
\*@args: 
\* max\_thread\_num ---> maximum of threads
\* pool ---> address of thread pool
\*@return value: 
\* 0 ---> create thread pool successfully
\* othres ---> create thread pool failed
\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/
 
int create\_tpool(tpool\_t\*\* pool,size\_t max_thread_num);
 
/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
\*@brief:
\* destroy thread pool
\*@args:
\* pool ---> address of pool
\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/
void destroy\_tpool(tpool\_t\* pool);
 
/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
\*@brief:
\* add tasks to thread pool
\*@args:
\* pool ---> thread pool
\* routine ---> entry function of each thread
\* args ---> arguments
\*@return value:
\* 0 ---> add ok
\* others ---> add failed 
\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/
int add\_task\_2\_tpool(tpool\_t\* pool,void\* (\*routine)(void\*),void\* args);
 
#endif//tpool.h

需要考虑的技术问题三,线程池的所有权应该交予谁。

这里我们需要考虑到,将线程池封装成一个so库是比较好的想法,那么,线程池的所有权就应该交予调用它的函数。所以我这里采取的就是这个方法。

tpool.c

#include "tpool.h"
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
 
 
static void\* work\_routine(void\* args)
{
   tpool\_t\* pool = (tpool\_t\*)args;
   tpool\_work\_t\* work = NULL;
 
   while(1){
        pthread\_mutex\_lock(&pool->queue_lock);
        while(!pool->tpool_head && !pool->shutdown){ // if there is no works and pool is not shutdown, it should be suspended for being awake
            pthread\_cond\_wait(&pool->queue_ready,&pool->queue_lock);
        }
 
        if(pool->shutdown){
           pthread\_mutex\_unlock(&pool->queue_lock);//pool shutdown,release the mutex and exit
           pthread\_exit(NULL);
        }
 
        /\* tweak a work\*/
        work = pool->tpool_head;
        pool->tpool_head = (tpool\_work\_t\*)pool->tpool_head->next;
        pthread\_mutex\_unlock(&pool->queue_lock);
 
        work->work\_routine(work->args);
 
        free(work);
   }
return NULL;
}
 
int create\_tpool(tpool\_t\*\* pool,size\_t max_thread_num)
{
   (\*pool) = (tpool\_t\*)malloc(sizeof(tpool\_t));
   if(NULL == \*pool){
        printf("in %s,malloc tpool\_t failed!,errno = %d,explain:%s\n",\_\_func\_\_,errno,strerror(errno));
        exit(-1);
   }
   (\*pool)->shutdown = 0;
   (\*pool)->maxnum_thread = max_thread_num;
   (\*pool)->thread_id = (pthread\_t\*)malloc(sizeof(pthread\_t)\*max_thread_num);
   if((\*pool)->thread_id == NULL){
        printf("in %s,init thread id failed,errno = %d,explain:%s",\_\_func\_\_,errno,strerror(errno));
        exit(-1);
   }
   (\*pool)->tpool_head = NULL;
   if(pthread\_mutex\_init(&((\*pool)->queue_lock),NULL) != 0){
        printf("in %s,initial mutex failed,errno = %d,explain:%s",\_\_func\_\_,errno,strerror(errno));
        exit(-1);
   }
 
   if(pthread\_cond\_init(&((\*pool)->queue_ready),NULL) != 0){
        printf("in %s,initial condition variable failed,errno = %d,explain:%s",\_\_func\_\_,errno,strerror(errno));
        exit(-1);
   }
 
   for(int i = 0; i < max_thread_num; i++){
        if(pthread\_create(&((\*pool)->thread_id[i]),NULL,work_routine,(void\*)(\*pool)) != 0){
           printf("pthread\_create failed!\n");
           exit(-1);
        }
   }
return 0;
}
 
void destroy\_tpool(tpool\_t\* pool)
{
   tpool\_work\_t\* tmp_work;
 
   if(pool->shutdown){
        return;
   }
   pool->shutdown = 1;
 
   pthread\_mutex\_lock(&pool->queue_lock);
   pthread\_cond\_broadcast(&pool->queue_ready);
   pthread\_mutex\_unlock(&pool->queue_lock);
 
   for(int i = 0; i < pool->maxnum_thread; i++){
        pthread\_join(pool->thread_id[i],NULL);
   }
   free(pool->thread_id);
   while(pool->tpool_head){
        tmp_work = pool->tpool_head;
        pool->tpool_head = (tpool\_work\_t\*)pool->tpool_head->next;
        free(tmp_work);
   }
 
   pthread\_mutex\_destroy(&pool->queue_lock);
   pthread\_cond\_destroy(&pool->queue_ready);
   free(pool);
}
 
int add\_task\_2\_tpool(tpool\_t\* pool,void\* (\*routine)(void\*),void\* args)
{
   tpool\_work\_t\* work,\*member;
 
   if(!routine){
        printf("rontine is null!\n");
        return -1;
   }
 
   work = (tpool\_work\_t\*)malloc(sizeof(tpool\_work\_t));
   if(!work){
        printf("in %s,malloc work error!,errno = %d,explain:%s\n",\_\_func\_\_,errno,strerror(errno));
        return -1;
   }
 
   work->work_routine = routine;
   work->args = args;
   work->next = NULL;
 
   pthread\_mutex\_lock(&pool->queue_lock);


![img](https://img-blog.csdnimg.cn/img_convert/1e84c35f63dad0d63ed0c1210d94a2e8.png)
![img](https://img-blog.csdnimg.cn/img_convert/2dc015e280b88716272167799020af8d.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**


[外链图片转存中...(img-ecFgczB5-1715791011157)]
[外链图片转存中...(img-RKn9L1Tf-1715791011157)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值