/*  写在前面的话:

    今天刚“开原”,选择了一篇关于线程池的文件与大家分享,希望能对您学习有所帮助,也希望能与大家共同学习!

    选择在这个特殊的时候注册并发文章也是有一些我个人特殊的意义的,看我的id(西游小学生.45)就知道了,哈哈。在这里也很感谢博客园的员工,刚发申请两分钟就同意了。

*/

  最近由于要写一个类似于QQ的程序,所以想到要用到多线程。既然要用多线程,那何不写一个线程池?于是上网搜了搜多线程的代码,发现大多都不是很完善,或者有些小bug。所以,在这里贴出一个完整的,经过我多重测试的,先贴上一个线程池的简单实现过程(固定大小的线程池),稍后和网络编程的结合过些天会再贴出来。这里引用Linux的一句话:“

talk is cheap show me the code

”。所以我的博客大部分都会给大家贴代码的,谢谢。

首先,不得不提的就是线程池的好处:

  简单来说,如果调用一个线程分为:

         t1创建线程

         t2完成作业

         t3销毁线程

  那么如果我们有100个作业要完成,总时间T=100*(t1+t2+t3)。但如果我们提前申请一个10个线程的固定大小线程池,那么完成作业的总时间为申请10个线程和销毁10个线程以及100个作业的时间,及T=10*t1+100*t2+10*t3。所以,线程池最大的优势就是节省了线程申请以及销毁的时间!

  下面是具体的代码实现:

  

首先,一般他会有两个数据结构,一个任务队列,一个线程池结构体
typedef struct tpool_work {                   任务队列
    void*               (*routine)(int);            任务函数,该任务的处理函数 
    int                arg;                                 传入任务函数的参数 
    struct tpool_work   *next;                   任务队列的下一个节点 
}tpool_work_t;

 

typedef struct tpool {                             线程池
    int             shutdown;                         线程池是否销毁 
    int             max_thr_num;                   最大线程数 
    pthread_t       *thr_id;                         线程ID数组 
    tpool_work_t    *queue_head;           任务队列的头结点 
    pthread_mutex_t queue_lock;          互斥锁          
    pthread_cond_t  queue_ready;         条件变量
}tpool_t;

第一步:创建线程池:申请线程池结构体(该结构体是一个全局变量,在这里给他初始化,即申请空间malloc),初始化变量(锁,条件变量),申请线程id数组,创建线程pthread_create,让线程执行pthead_run函数,(使用条件变量前会加锁,条件变量wait时会自动解锁) run函数中会使用pthread_cond_wait等待条件变量被唤醒
第二步:增加工作:申请任务节点,malloc,并初始化,将参数传过来的任务函数,函数参数赋值给任务节点结构体,然后将任务挂到全局变量线程池的任务队列上,(此操作需要加锁再解锁)并使用pthread_cond_signal,给条件变量发信号
第三部:run函数接收到信号后会继续执行,此时获取到全局变量线程池任务队列头结点,取得任务节点执行对应函数即可,执行完while(1)回到条件变量的pthread_cond_wait处

/*************************************************************************
> File Name: tpool.h
> Author: 
> Mail: 
> Created Time: 2015年04月01日 星期三 17时34分00秒
************************************************************************/

#ifndef THREAD_POOL_H__
#define THREAD_POOL_H__

#include <pthread.h>

/* 要执行的任务链表 */
typedef struct tpool_work {
void* (*routine)(int); /* 任务函数 */
int arg; /* 传入任务函数的参数 */
struct tpool_work *next; 
}tpool_work_t;

typedef struct tpool {
int shutdown; /* 线程池是否销毁 */
int max_thr_num; /* 最大线程数 */
pthread_t *thr_id; /* 线程ID数组 */
tpool_work_t *queue_head; /* 线程链表 */
pthread_mutex_t queue_lock; 
pthread_cond_t queue_ready; 
}tpool_t;

/*
* @brief 创建线程池 
* @param max_thr_num 最大线程数
* @return 0: 成功 其他: 失败 
*/
int
tpool_create(int max_thr_num);

/*
* @brief 销毁线程池 
*/
void
tpool_destroy();

/*
* @brief 向线程池中添加任务
* @param routine 任务函数指针
* @param arg 任务函数参数
* @return 0: 成功 其他:失败 
*/
int
tpool_add_work(void*(*routine)(int), int arg);

#endif

/*************************************************************************
> File Name: func.c
> Author: 
> Mail: 
> Created Time: 2015年04月01日 星期三 17时35分56秒
************************************************************************/

#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include "tpool.h"

static tpool_t *tpool = NULL;

void 
print_work(tpool_work_t *head) //输出任务列队当前工作
{
tpool_work_t *work= head;
printf("\n当前任务列队里还有的任务是:(任务的参数是)\n");
while(work){
printf("%d ",work->arg);
work = work->next;
}
printf("\n");
}

/* 工作者线程函数, 从任务链表中取出任务并执行 */
static void* 
thread_routine(void *arg)
{
tpool_work_t *work; //任务链表

while(1) {
/* 如果线程池没有被销毁且没有任务要执行,则等待 */
pthread_mutex_lock(&tpool->queue_lock); //锁住线程池
while(!tpool->queue_head && !tpool->shutdown) {
pthread_cond_wait(&tpool->queue_ready, &tpool->queue_lock);
}
if (tpool->shutdown) { //0没有注销
pthread_mutex_unlock(&tpool->queue_lock);
pthread_exit(NULL);
}
sleep(3);
// print_work(tpool->queue_head);
//取出任务链表头的一个工作
work = tpool->queue_head;
tpool->queue_head = tpool->queue_head->next;
pthread_mutex_unlock(&tpool->queue_lock);

work->routine(work->arg);
free(work);
}

return NULL; 
}

/*
* 创建线程池 
*/
int
tpool_create(int max_thr_num)
{
int i;

tpool = calloc(1, sizeof(tpool_t)); //线程池tpool
if (!tpool) {
printf("%s: calloc failed\n", __FUNCTION__);
exit(1);
}

/* 初始化 */
tpool->max_thr_num = max_thr_num; //初始化最大线程池数
tpool->shutdown = 0; //线程池注销设为0未注
tpool->queue_head = NULL; //线程池链表
if (pthread_mutex_init(&tpool->queue_lock, NULL) !=0) { //初始化线程锁
printf("%s: pthread_mutex_init failed, errno:%d, error:%s\n",
__FUNCTION__, errno, strerror(errno));
exit(1);
}
if (pthread_cond_init(&tpool->queue_ready, NULL) !=0 ) { //初始化条件变量
printf("%s: pthread_cond_init failed, errno:%d, error:%s\n", 
__FUNCTION__, errno, strerror(errno));
exit(1);
}

/* 创建工作者线程 */
tpool->thr_id = calloc(max_thr_num, sizeof(pthread_t));//申请线程ID数组
if (!tpool->thr_id) {
printf("%s: calloc failed\n", __FUNCTION__);
exit(1);
}
for (i = 0; i < max_thr_num; ++i) {
if (pthread_create(&tpool->thr_id[i], NULL, thread_routine, NULL) != 0){
printf("%s:pthread_create failed, errno:%d, error:%s\n", __FUNCTION__, 
errno, strerror(errno));
exit(1);
}

}

return 0;
}

/* 销毁线程池 */
void
tpool_destroy()
{
int i;
tpool_work_t *member;

if (tpool->shutdown) {
return;
}

tpool->shutdown = 1;
/* 通知所有正在等待的线程 */
pthread_mutex_lock(&tpool->queue_lock);
pthread_cond_broadcast(&tpool->queue_ready);
pthread_mutex_unlock(&tpool->queue_lock);
for (i = 0; i < tpool->max_thr_num; ++i) {
pthread_join(tpool->thr_id[i], NULL);
}
free(tpool->thr_id);

while(tpool->queue_head) {
member = tpool->queue_head;
tpool->queue_head = tpool->queue_head->next;
free(member);
}

pthread_mutex_destroy(&tpool->queue_lock); 
pthread_cond_destroy(&tpool->queue_ready);

free(tpool); 
}

/* 向线程池添加任务 */
int
tpool_add_work(void*(*routine)(int), int arg)
{
tpool_work_t *work, *member;

if (!routine){
printf("%s:Invalid argument\n", __FUNCTION__);
return -1;
}

work = malloc(sizeof(tpool_work_t)); //申请一个任务结点
if (!work) { //申请失败
printf("%s:malloc failed\n", __FUNCTION__);
return -1;
}
work->routine = routine; //线程执行函数设为main中传进来的参数
work->arg = arg; //线程执行函数的参数设置为main中传进来的参数
work->next = NULL;

pthread_mutex_lock(&tpool->queue_lock); //锁住线程池锁 
member = tpool->queue_head; 
if (!member) { //找到线程链表中最后一个结点,并该工作放置于线程链表尾
tpool->queue_head = work;
} else {
while(member->next) {
member = member->next;
}
member->next = work;
}
/* 通知工作者线程,有新任务添加 */
pthread_cond_signal(&tpool->queue_ready);
pthread_mutex_unlock(&tpool->queue_lock);

return 0; 
}

void *func(int arg)
{
printf("thread %d threadID is: %d\n",arg,(int)pthread_self());
print_work(tpool->queue_head);
sleep(3);
return NULL;
}

int
main(int arg, char **argv)
{
if (tpool_create(5) != 0) {
printf("tpool_create failed\n");
exit(1);
}
int i;
for (i = 0; i < 10; ++i) {
tpool_add_work(func, i);
// usleep(1);
}
sleep(10); //如果sleep无或时间太短,注意主线程可能先于其他线程完成导致部分作业未完成
tpool_destroy();
return 0;
}

Linux下简单的多线程编程--线程池的实现的更多相关文章

  1. linux下C语言多线程编程实例

    用一个实例.来学习linux下C语言多线程编程实例. 代码目的:通过创建两个线程来实现对一个数的递加.代码: //包含的头文件 #include <pthread.h> #include ...

  2. linux下C++的多线程编程

    1. 引言 线程(thread)技术早在60年代就被提出,但真正应用多线程到操作系统中去,是在80年代中期,solaris是这方面的佼佼者.传统的Unix也支持线程的概念,但是在一个进程(proces ...

  3. Linux程序设计学习笔记----多线程编程线程同步机制之相互排斥量(锁)与读写锁

    相互排斥锁通信机制 基本原理 相互排斥锁以排他方式防止共享数据被并发訪问,相互排斥锁是一个二元变量,状态为开(0)和关(1),将某个共享资源与某个相互排斥锁逻辑上绑定之后,对该资源的訪问操作例如以下: ...

  4. Java多线程与线程池技术

    一.序言 Java多线程编程线程池被广泛使用,甚至成为了标配. 线程池本质是池化技术的应用,和连接池类似,创建连接与关闭连接属于耗时操作,创建线程与销毁线程也属于重操作,为了提高效率,先提前创建好一批 ...

  5. Linux下的C Socket编程 -- server端的简单示例

    Linux下的C Socket编程(三) server端的简单示例 经过前面的client端的学习,我们已经知道了如何创建socket,所以接下来就是去绑定他到具体的一个端口上面去. 绑定socket ...

  6. Linux下的C Socket编程 -- server端的继续研究

    Linux下的C Socket编程(四) 延长server的生命周期 在前面的一个个例子中,server在处理完一个连接后便会立即结束掉自己,然而这种server并不科学啊,server应该是能够一直 ...

  7. C#多线程之线程池篇3

    在上一篇C#多线程之线程池篇2中,我们主要学习了线程池和并行度以及如何实现取消选项的相关知识.在这一篇中,我们主要学习如何使用等待句柄和超时.使用计时器和使用BackgroundWorker组件的相关 ...

  8. CreateThread简单那多线程编程

    CreateThread简单那多线程编程 作者:vpoet mail:vpoet_sir@163.com 在进行多任务处理的时候我们往往会用到多线程技术,多线程理论上是多个线程同事处理不同的工作,但是 ...

  9. Qt中的多线程与线程池浅析+实例

    1. Qt中的多线程与线程池 今天学习了Qt中的多线程和线程池,特写这篇博客来记录一下 2. 多线程 2.1 线程类 QThread Qt 中提供了一个线程类,通过这个类就可以创建子线程了,Qt 中一 ...

随机推荐

  1. 初学spring(二)

      1.spring推荐使用接口编程,配合di可以达到层与层之间解耦

  2. 腾讯云 net.core

    搭建 .NET Core 开发环境 安装 .Net Core 执行代码 任务时间:时间未知 .NET Core 的官方文档很详细,本实验带你建立一个.NET Core 1.1的Web运行环境,更多内容 ...

  3. spark-streaming-kafka-0-10源码分析

    转发请注明原创地址http://www.cnblogs.com/dongxiao-yang/p/7767621.html 本文所研究的spark-streaming代码版本为2.3.0-SNAPSHO ...

  4. iOS开发多线程篇 09 —NSOperation简单介绍

    iOS开发多线程篇—NSOperation简单介绍 一.NSOperation简介 1.简单说明 NSOperation的作⽤:配合使用NSOperation和NSOperationQueue也能实现 ...

  5. java中的codereview

     关于codereview,在平时的开发中,经常忽略的环节,参照目前介绍写好代码的几本书和之前掉进的坑,做了一个总结,分享出来. 为什么要做 通过review规避一些代码层面的问题 提升可读性,方 ...

  6. hibernate查询之后用el表达式取值时遇到的问题

    String juniorApprovalUserHql = "select c.id,c.username from UserInfo c left join c.userRole whe ...

  7. hdu5795 A Simple Nim 求nim求法,打表找sg值规律 给定n堆石子,每堆有若干石子,两个人轮流操作,每次操作可以选择任意一堆取走任意个石子(不可以为空) 或者选择一堆,把它分成三堆,每堆不为空。求先手必胜,还是后手必胜。

    /** 题目:A Simple Nim 链接:http://acm.hdu.edu.cn/showproblem.php?pid=5795 题意:给定n堆石子,每堆有若干石子,两个人轮流操作,每次操作 ...

  8. GCD - Extreme (II) for(i=1;i<N;i++) for(j=i+1;j<=N;j++) { G+=gcd(i,j); } 推导分析+欧拉函数

    /** 题目:GCD - Extreme (II) 链接:https://vjudge.net/contest/154246#problem/O 题意: for(i=1;i<N;i++) for ...

  9. https原理与实践

    HTTPS 原理与证书实践   分类: Web应用   1.1 网络安全知识 1.1.1 网结安全出现背景 网络就是实现不同主机之间的通讯,网络出现之初利用TCP/IP协议簇的相关协议概念,已经满足了 ...

  10. mysql的两个备份语句

    适合多引擎混合(例如:myisam与innodb混合)的备份命令如下: mysqldump -A -R --triggers --master-data=2 --single-transaction  ...