C语言线程池的实现

转载地址:http://blog.csdn.net/u014453898/article/details/53764720

(一)线程池的组成:

           两个结构体: 任务结构体 和 线程池结构体

           定义如下:

          任务结构体:

          任务结构体有三个变量,分别是 任务执行的函数(函数指针),赋给任务的参数,任务的next指针。

[cpp]  view plain  copy
  1. struct task   
  2. {  
  3.     void *(*task)(void *arg);   //指针函数(即这个任务要去做什么事情)  
  4.     void *arg;          //参数  
  5.     struct task *next;      //指向下一个任务的指针  
  6.       
  7. };  


           线程池结构体:

           注意:一个线程池结构体一共有 7个成员变量。

[cpp]  view plain  copy
  1. typedef struct thread_pool  
  2. {  
  3.     pthread_mutex_t lock;//互斥锁  
  4.     pthread_cond_t cond;//条件变量  
  5.     struct task *task_list;//任务链表  
  6.       
  7.     pthread_t *tids;    //存储创建线程id号  
  8.       
  9.     unsigned waiting_tasks;     //任务队列中正在等待的任务个数  
  10.     unsigned active_threads;    //正在工作的线程个数  
  11.     bool shutdown;          //开关标志  
  12.       
  13. }thread_pool;  


(二)线程池的操作函数:

        (1)线程池初始化函数:(输入的变量:  指向线程池的指针,创建的线程数目)

                初始化互斥锁,初始化条件变量,初始化任务链表,初始化 waiting_tashs为0,初始化 active_threads 为最大线程值,初始化要创建的线程,shutdown为 false

              

                步骤:

                 1.初始化互斥锁,条件变量

                 2.初始化任务链表表头,并把链表头 的next指针设成 NULL

                 3.等待任务数设置为0. 工作线程数设置为 threads_num

                 4/开关标志位设置为 falsh

                 5.for循环创建线程


[cpp]  view plain  copy
  1. bool pool_init(thread_pool *pool,unsigned int threads_num)//初始化线程池  
  2. {  
  3.     int i;  
  4.     pthread_mutex_init(&pool->lock,NULL);  
  5.     pthread_cond_init(&pool->cond,NULL);  
  6.       
  7.     pool->task_list=malloc(sizeof(struct task));       //初始化任务链表  
  8.     pool->waiting_tasks=0;  
  9.     pool->active_threads=threads_num;  
  10.     pool->shutdown=false;  
  11.   
  12.     pool->tids=malloc(sizeof(pthread_t)*MAX_ACTIVE_THREADS);       //MAX_ACTIVE_THREADS为一个宏,表示要能容纳的最大线程数  
  13.     if(pool->task_list==NULL||pool->tids==NULL)  
  14.     {  
  15.         perror("allocate memory error");  
  16.         return false;  
  17.           
  18.     }  
  19.     pool->task_list->next=NULL;  
  20.       
  21.     for(i=0;i<pool->active_threads;i++)  
  22.     {  
  23.         if(pthread_create(&((pool->tids)[i]),NULL,routine,(void *)pool)!=0)  
  24.         {  
  25.             perror("create thread failed!\n");  
  26.             return false;  
  27.               
  28.         }  
  29.           
  30.     }  
  31.       
  32.     return true;  
  33.       
  34. }  


(3)往线程池新增任务:

输入参数:线程池指针,任务要处理的函数指针,给任务的参数

步骤:

       1.创建新的任务结点,并把他的next置空

       2.给任务结点赋参数(指针函数,给任务的参数arg)

       3.给互斥锁上锁,因为准备操作共享资源(任务链表)

       4.把新建的任务结点插入到任务链表的结尾,线程池的等待任务数目+1

       5.互斥锁解锁。

[cpp]  view plain  copy
  1. bool add_task(thread_pool *pool,void *(*task)(void *arg),void *arg)  
  2. {  
  3.     struct task *new_task=malloc(sizeof(struct task));  
  4.     if(new_task==NULL)  
  5.     {  
  6.         perror("allocate memory error");  
  7.         return false;  
  8.           
  9.     }  
  10.       
  11.     new_task->task=task;//任务存入新节点  
  12.     new_task->arg=arg;  
  13.     new_task->next=NULL;  
  14.       
  15.       
  16.     pthread_mutex_lock(&pool->lock);  
  17.     if(pool->waiting_tasks>=MAX_WAITING_TASKS)  
  18.     {  
  19.         pthread_mutex_unlock(&pool->lock);  
  20.         fprintf(stderr,"too many tasks.\n");//任务太多添加失败  
  21.         free(new_task);  
  22.         return false;  
  23.   
  24.     }  
  25.     //遍历链表,找到队列尾结点,以便把新结点加入  
[cpp]  view plain  copy
  1.     struct task *tmp=pool->task_list;  
  2.     while(tmp->next!=NULL)  
  3.         tmp=tmp->next;  
  4.           
  5.     tmp->next=new_task;  
  6.     pool->waiting_tasks++;  
  7.     pthread_mutex_unlock(&pool->lock);           //解互斥锁  
  8.     pthread_cond_signal(&pool->cond);            //解锁后唤醒一个阻塞在条件变量的线程,因为线程被创建后执行的函数都会默认被阻塞  
  9.     return true;  
  10.       
  11. }  

(4)往线程池新增线程
[cpp]  view plain  copy
  1. int add_thread(thread_pool *pool, unsigned additional_threads)  
  2. {  
  3.     //增加线程数判断:如增加0条线程,则直接退出该函数  
  4.     if(additional_threads == 0)  
  5.         return 0;  
  6.       
  7.     //计算总线程数,用于循环创建线程时使用  
  8.     unsigned total_threads = pool->active_threads + additional_threads;  
  9.   
  10.     int i, actual_increment = 0;             //actual_increment 为实际上增加的线程数  
  11.       
  12.     //循环创建指定数目的线程:不能超过最大线程数  
  13.     for(i = pool->active_threads;  
  14.         i < total_threads && i < MAX_ACTIVE_THREADS; i++)  
  15.     {  
  16.         //创建线程,并判断是否创建成功,失败提示并退出该函数  
  17.         if(pthread_create(&((pool->tids)[i]),  
  18.                 NULL, thread_routine, (void *)pool) != 0)  
  19.         {  
  20.             perror("add threads error");  
  21.   
  22.             if(actual_increment == 0)  
  23.                 return -1;  
  24.   
  25.             break;  
  26.         }  
  27.           
  28.         //实际新增加的线程数  
  29.         actual_increment++;   
  30.     }  
  31.       
  32.     //计算增加指定数目的线程后,线程池活动线程数加上实际成功增加的线程数  
  33.     pool->active_threads += actual_increment;  
  34.     return actual_increment;  
  35. }  

(5)往线程池删除线程
[cpp]  view plain  copy
  1. int remove_thread(thread_pool *pool,unsigned int removing_threads)  
  2. {  
  3.     if(removing_threads==0)  
  4.     {  
  5.         return pool->active_threads;  
  6.     }  
  7.     int remain_threads=pool->active_threads-removing_threads;  
  8.     remain_threads=remain_threads>0 ? remain_threads:1;  
  9.     int i;  
  10.     for(i=pool->active_threads-1;i>remain_threads-1;i--)     
  11.     {  
  12.         errno=pthread_cancel(pool->tids[i]);              //删除线程是按照线程创建的顺序来删的  
  13.         if(errno!=0)                                      //线程池中的线程不一定都能删除的,  
  14.         {                         //因为在某线程在执行任务时,有个函数会让他暂时不能被删除  
  15.             break;                    //下面会有解释的  
  16.         }                                             //所以这里就算pthread_cancel失败,也是break,而不是return  
  17.     }  
  18.     if(i==pool->active_threads-1)  
  19.         return -1;  
  20.     else   
  21.     {  
  22.         pool->active_threads=i+1;  
  23.         return i+1;  
  24.           
  25.     }  
  26.       
  27. }  

(6)销毁线程池
[cpp]  view plain  copy
  1. bool destroy_pool(thread_pool *pool)  
  2. {  
  3.     pool->shutdown = true;  
  4.     pthread_cond_broadcast(&pool->cond);           //唤醒因条件变量沉睡的所有线程  
  5.     int i;  
  6.     for(i=0;i<pool->active_threads;i++)  
  7.     {  
  8.         errno=pthread_join(pool->tids[i],NULL);      //等待线程回收完毕  
  9.         if(errno!=0)  
  10.         {  
  11.             printf("join tids[%d] error:%s\n",i,strerror(errno));  
  12.               
  13.               
  14.         }  
  15.         else  
  16.             printf("[%u] is joined\n",(unsigned)pool->tids[i]);  
  17.     }  
  18.     free(pool->task_list);  
  19.     free(pool->tids);  
  20.     free(pool);  
  21.     return true;  
  22. }  

(7)线程创建后执行的函数,即线程功能函数(重点)

[cpp]  view plain  copy
  1. void *routine(void *arg)  
  2. {  
  3.     thread_pool *pool = (thread_pool*)arg;  
  4.     struct task *p;  
  5.     while(1)  
  6.     {  
  7.         //从任务链表(head->next)中扣去节点  
  8.         pthread_cleanup_push(handler,(void *)&pool->lock);  
  9.         pthread_mutex_lock(&pool->lock);  
  10.           
  11.         //判断任务链表中是否有节点,没有就阻塞当前线程  
  12.         while(pool->waiting_tasks == 0 && !pool->shutdown)          //这个循环的作用是,当没任务时让线程沉睡,有任务时循环取任务  
  13.         {  
  14.             pthread_cond_wait(&pool->cond,&pool->lock);            //让线程沉睡  
  15.         }  
  16.         if(pool->waiting_tasks==0&&pool->shutdown==true)            //shutdown=true也要等任务全执行完了才能删除线程池  
  17.         {  
  18.               
  19.             pthread_mutex_unlock(&pool->lock);  
  20.             pthread_exit(NULL);  
  21.         }  
  22.         p =pool->task_list->next;  
  23.         pool->task_list->next =p->next;  
  24.         pool->waiting_tasks--;  
  25.           
  26.         //解锁  
  27.         pthread_mutex_unlock(&pool->lock);  
  28.         pthread_cleanup_pop(0);  
  29.           
  30.           
  31.         //处理刚才扣下来的那个节点任务  
  32.         pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);       //禁止当前这个线程被其他线程调用pthread_cancel来删除  
  33.         (p->task)(p->arg);                                       //执行任务  
  34.         pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);        //取消禁止删除效果  
  35.         //释放定义的那个节点  
  36.         free(p);  
  37.           
  38.     }  
  39.     pthread_exit(NULL);//退出  
  40. }  
上图中 pthread_cleanup_push 和 pthread_cleanup_pop的作用:
 线程有两种退出方式,有可能自己退出的,这是正常的退出方式,也有可能是非正常退出的,例如被其他线程取消,
试想一下,如果一个线程已经上锁了,还没解锁就被其他线程取消了,那这个锁就一直锁着,没办解开。因为就需要在互斥锁上锁前使用
pthread_cheanup_push(),这个函数可以在线程在非正常退出时,丢用指定函数来进行相应操作(解锁)
若线程并没有在互斥锁上锁时被非正常退出,就要调用 pthread_cleanup_pop()来取消pthread_cleanup_push()的操作。pthread_cleanup_pop一般用再解锁之后
所以这两个函数都是一对出现的

(8)线程池工作的基本逻辑:
1.首先 线程池初始化时 会创建出很多条线程,但他们没任务可执行时,就会调用条件变量cond,让自己沉睡。因此线程池一被创建,就躺着很多条沉睡的线程。
2.线程的执行函数中,有个while(1)循环,让线程有任务时执行任务,没任务时就调用pthread_cond_wait()来沉睡。
3.当有任务加入线程池的任务列表时,会通过调用pthread_cond_signal()来唤醒一条线程(add_task()函数),然后线程执行完就继续执行下一条任务或沉睡。
4.当线程池中的 shundown变量变成true时,便会调用pthread_cond_broadcase()唤醒所有沉睡的线程,使线程们自己退出



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值