Linux C 一个简单的线程池程序设计
最近在学习linux下的编程,刚开始接触感觉有点复杂,今天把线程里比较重要的线程池程序重新理解梳理一下。
实现功能:创建一个线程池,该线程池包含若干个线程,以及一个任务队列,当有新的任务出现时,如果任务队列不满,则把该任务加入到任务队列中去,并且向线程发送一个信号,调用某个线程为任务队列中的任务服务。如果线程池中的线程都在忙,那么任务队列中的任务则等待。本程序较为简单,把任务定义为了两个数相加,输出它们的和。
采用自顶向下的设计方法,先把整体框架构建出来,然后再慢慢把细节,小模块补全。
1.在linux环境下构建三个文件夹(include,src,bin)
include:包含该程序所需要的头文件。
src:包括程序的main函数,以及其他函数。
bin:程序编译,链接生成的可执行函数。
2.编写include下的头文件函数
vim pool_common.h
#ifndef _COMM
#define _COMM
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>
#include<sys/stat.h>
#include<sys/select.h>
#include<sys/time.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/types.h>
struct tag
{
int left;
int right;
};
typedef struct tag elem_t; //定义数据成员,即要输入的两个相加的数
#endif
vim pool_que.h
#ifndef _QUE
#define _QUE
#include "pool_common.h"//有关任务队列的函数
typedef enum
{
EMPTY,FULL,NEITHER
}STATUS; //枚举类型,判断队列是否为空返回的状态
typedef struct que
{
elem_t *que_arr; //任务队列中的任务数组,储存数据
int que_front; //任务队列的队头
int que_tail; //任务队列的队尾
int que_capacity; //任务队列所容纳任务的数量
pthread_mutex_t que_lock; //任务队列的互斥锁
pthread_cond_t que_pro; //任务产生的信号
pthread_cond_t que_con; //任务被执行的信号
}que_t,*pque_t;
void que_init(pque_t pq,int sizenum);//任务队列初始化 STATUS que_full(pque_t pq); //判断队列是否满了
STATUS que_empty(pque_t pq); //判断队列是否为空
void que_push(pque_t pq,elem_t val); //将新的任务加入队列
void que_pop(pque_t pq); //将任务从队列中删除
elem_t que_top(pque_t pq); //获得任务队首的任务,和上个函数结合使用,即将任务交于线程执行
void que_destroy(pque_t pq); //销毁队列
#endif
vim pool_thread.h //关于多线程的头文件
#ifndef _POOL_THREAD
#define _POOL_THREAD
#include "pool_que.h"
typedef void* (*consumer_handle)(void*); //定义线程所以执行的任务函数名称
typedef struct threads
{
que_t pool_que; //线程池中的任务队列
int pool_cnt; //线程池中线程个数
pthread_t *pool_arr; //线程数组
consumer_handle pool_handle; //线程索要处理的任务函数
}threadspool_t,*pthreadspool_t;
void pool_init(pthreadspool_t ppool,int quecapacity,int threadsnum,consumer_handle hd);//初始化多线程
void pool_on(pthreadspool_t ppool);//启动多线程
void pool_off(pthreadspool_t ppool);//关闭多线程
void pool_put(pthreadspool_t ppool,elem_t val);//将新的任务加入到线程池中
void pool_get(pthreadspool_t ppool,elem_t *val);//取出线程池中的任务
void pool_destroy(pthreadspool_t ppool);//销毁线程池
#endif
3.编写main函数
cd src
vim pool_main.c
#include "pool_pthread.h"
void *handle(void * arg)//任务处理函数
{
pthreadspool_t ppool=(pthreadspool_t)arg;
elem_t val;
while()
{
pool_get(ppool,&val); //从线程池中获得任务数据
printf("%u:excute task!,%d +%d=%d\n",pthread_self(),val.left,val.right,val.left+val.right);//执行输出
} }
int main(int argc,char *argv[])
{
if(argc!=)
{
printf("EXE QUE_CAPACITY THREADS_NUM!\n");
exit();
}//生成的可执行函数后面必须加上2个变量,否则出错
threadspool_t mypool;
pool_init(&mypool,atoi(argv[]),atoi(argv[]),handle);//线程池初始化
pool_on(&mypool);//启动线程池
char buf[];
//下面是一个select函数,其主要作用是每隔一秒去监听键盘上是否有数字输入,如果有,那么把数据流中的数据转化为任务数据添加到任务队列中去
/* while(1)
{
fd_set rds;
FD_ZERO(&rds); //初始化
FD_SET(0,&rds);//rds捆绑输入,监听 struct timeval tm;
tm.tv_sec=1;
tm.tv_usec=0;//设置时间
if(0==select(1024,&rds,NULL,NULL,&tm))
{
continue;
}//如果没输入,继续监听
if(FD_ISSET(0,&rds))
{
memset(buf,0,sizeof(buf));
if(read(0,buf,127)==0)//将键盘上输入的字符流储存到数组中去
{
break;
}
elem_t val;
sscanf(buf,"%d%d",&val.left,&val.right);//将数组中的字符赋值两个数字到数据项
pool_put(&mypool,val);//加入任务队列
}
}*/
//第二种输入方法,不停的输入数字,并将这些数据添加到任务队列中去,不用select函数
int i,x,y;
elem_t val;
while()
{
scanf("%d%d",&x,&y);
val.left=x;
val.right=y;
pool_put(&mypool,val);
}
pool_off(&mypool);//关闭多线程
pool_destroy(&mypool);//销毁线程池
return ;
}
4.实现多线程的具体功能函数pool_thread.c和队列操作的具体功能函数pool_que.c
vim pool_thread.c
#include<pool_pthread.h>
void pool_init(pthreadspool_t ppool,int quecapacity,int threadsnum,consumer_handle hd)
{
que_init(&ppool->pool_que,quecapacity);
ppool->pool_arr=(pthread_t *)calloc(threadsnum,sizeof(pthread_t));
ppool->pool_cnt=threadsnum;//线程数
ppool->pool_handle=hd;
}
void pool_on(pthreadspool_t ppool)
{
int i;
for(i=;i<ppool->pool_cnt;i++)
{
if(!=pthread_create(ppool->pool_arr+i,NULL,ppool->pool_handle,(void*)ppool))//创建线程,并且执行
{
printf("thread create fail!\n");
exit();
}
}
}
void pool_off(pthreadspool_t ppool)
{
int i;
for(i=;i<ppool->pool_cnt;i++)
{
pthread_join(ppool->pool_arr[i],NULL);//关闭线程
}
}
void pool_put(pthreadspool_t ppool,elem_t val)
{
que_push(&ppool->pool_que,val);
}
void pool_get(pthreadspool_t ppool,elem_t* val)
{
*val=que_top(&ppool->pool_que);
que_pop(&ppool->pool_que);
}
void pool_destroy(pthreadspool_t ppool)
{
que_destroy(&ppool->pool_que);
free(ppool->pool_arr);
}
vim pool_que.c
#include "pool_que.h"
void que_init(pque_t pq,int sizenum)
{
pq->que_arr=(elem_t *)calloc(sizenum,sizeof(elem_t));
pq->que_capacity=sizenum;
pq->que_front =;
pq->que_tail=;
pthread_mutex_init(&pq->que_lock,NULL);//初始化互斥锁
pthread_cond_init(&pq->que_pro,NULL);//初始化信号量
pthread_cond_init(&pq->que_con,NULL);
} STATUS que_empty(pque_t pq)
{
if(pq->que_front==pq->que_tail)//队首和队尾相等则为空,该队列是一个循环队列
{
return EMPTY;
}
else
return NEITHER;
}
STATUS que_full(pque_t pq)
{
if((pq->que_tail+)%pq->que_capacity==pq->que_front)
{
return FULL;
}
else
return NEITHER;
} void que_push(pque_t pq,elem_t val)
{
pthread_mutex_lock(&pq->que_lock);//关闭互斥锁
while(que_full(pq)==FULL)
{
pthread_cond_wait(&pq->que_pro,&pq->que_lock); //当任务队列为满时,等待执行任务的信号,并且将互斥锁打开
}
pq->que_arr[pq->que_tail]=val;
pq->que_tail=(pq->que_tail+)%pq->que_capacity;
pthread_cond_signal(&pq->que_con);//通知线程有新任务出现
pthread_mutex_unlock(&pq->que_lock);//打开互斥锁
}
elem_t que_top(pque_t pq)
{
pthread_mutex_lock(&pq->que_lock);//上锁
while(EMPTY==que_empty(pq))
{
pthread_cond_wait(&pq->que_con,&pq->que_lock);
}
return pq->que_arr[pq->que_front];
}
void que_pop(pque_t pq)
{
pq->que_front=(pq->que_front+)%pq->que_capacity;
pthread_cond_signal(&pq->que_pro);
pthread_mutex_unlock(&pq->que_lock);
}
void que_destroy(pque_t pq)
{
free(pq->que_arr);
pthread_mutex_destroy(&pq->que_lock);//销毁锁和信号
pthread_cond_destroy(&pq->que_pro);
pthread_cond_destroy(&pq->que_con);
}
5.Makefile函数
由于程序涉及的函数较多,为了编译执行方便,可以使用Makefile函数,用make命令来执行
EXE_DIR:=./bin //生成可执行函数的地址
INC_DIR:=./include //头文件
SRC_DIR:=./src //源文件 OBJECTS:=./src/pool_main.c ./src/pool_thread.c ./src/pool_que.c $(EXE_DIR)/main:$(OBJECTS) $(INCLUDES)
gcc -g -o $@ $(OBJECTS) -I$(INC_DIR) -lpthread//编译命令
6.执行

7.总结
这个程序只是实现了一个简单的加法运算,当然也可以把多线程执行的任务换成其他的任务。只是通过这一个实例来说明多线程的意义和构建过程。
线程池:创建线程池以后,首先创建若干个线程。当任务队列中有任务出现时,指定一个线程为这个任务服务,若每个线程都在执行,那么任务队列中的任务只能等待。反之,如果任务队列为空,线程也只能等待。在客户端向服务器请求过程中,虽然线程不像进程那样占用很多资源,但是线程本身的创建和销毁在线程数量多的情况下仍然是很大的工作量。线程池实现了线程的复用,每个线程执行完一个任务后,可以继续执行另一个任务,这大大提高了线程处理任务的效率。
Linux C 一个简单的线程池程序设计的更多相关文章
- linux网络编程-一个简单的线程池(41)
有时我们会需要大量线程来处理一些相互独立的任务,为了避免频繁的申请释放线程所带来的开销,我们可以使用线程池 1.线程池拥有若干个线程,是线程的集合,线程池中的线程数目有严格的要求,用于执行大量的相对短 ...
- Linux C 实现一个简单的线程池
线程池的定义 线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务.线程池线程都是后台线程.每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中.如 ...
- Java一个简单的线程池实现
线程池代码 import java.util.List; import java.util.Vector; public class ThreadPool { private static ...
- 【Linux】一个简单的线程创建和同步的例子
最近很多精力在Linux上,今天简单看了一下Linux上的线程和同步,其实不管windows还是Linux,OS层面的很多原理和概念都是相同的,很多windows之上的经验和概念完全可以移植到Linu ...
- 【C/C++开发】C++实现简单的线程池
C++实现简单的线程池 线程池编程简介: 在我们的服务端的程序中运用了大量关于池的概念,线程池.连接池.内存池.对象池等等.使用池的概念后可以高效利用服务器端的资源,比如没有大量的线程在系统中进行上下 ...
- Linux下一个简单的日志系统的设计及其C代码实现
1.概述 在大型软件系统中,为了监测软件运行状况及排查软件故障,一般都会要求软件程序在运行的过程中产生日志文件.在日志文件中存放程序流程中的一些重要信息, 包括:变量名称及其值.消息结构定义.函数返回 ...
- Java网络与多线程系列之1:实现一个简单的对象池
前言 为什么要从对象池开始呢,先从一个网络IO操作的demo说起 比如下面这段代码,显而易见已经在代码中使用了一个固定大小的线程池,所以现在的重点在实现Runnble接口的匿名对象上,这个对象每次创建 ...
- 简单C++线程池
简单C++线程池 Java 中有一个很方便的 ThreadPoolExecutor,可以用做线程池.想找一下 C++ 的类似设施,尤其是能方便理解底层原理可上手的.网上找到的 demo,基本都是介绍的 ...
- ExecutorService 建立一个多线程的线程池的步骤
ExecutorService 建立一个多线程的线程池的步骤: 线程池的作用: 线程池功能是限制在系统中运行的线程数. 依据系统的环境情况,能够自己主动或手动设置线程数量.达到执行的最佳效果:少了浪费 ...
随机推荐
- block的修饰词为什么选用copy
想必很多开发人员知道一般用copy修饰block,接下来就讲解为什么需要用copy,甚至会讲到其实用strong修饰block也是可以的 在 Objective-C 语言中,一共有 3 种类型的 bl ...
- 自己动手实现一个简单的JSON解析器
1. 背景 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.相对于另一种数据交换格式 XML,JSON 有着诸多优点.比如易读性更好,占用空间更少等.在 ...
- DEDE中 field:rel 是什么意思,起一个什么样的作用效果
DEDE中 field:rel 是什么意思,起一个什么样的作用效果 这是一段调用导航栏目的代码 {dede:channel type='top' row='10' } [field:typename/ ...
- 借助 Vue 来构建单页面应用
原文: https://github.com/MeCKodo/vue-tutorial 主题 Vue.js (1/2)Vue构建单页应用最佳实战 前言 我们将会选择使用一些vue周边的库 1.使用no ...
- Windows和Linux如何使用Java代码实现关闭进程
在用selenium做自动化测试时,由于各种不明原因,有时Chrome浏览器会出现假死的情况,也就是整个浏览器响应超时,本人脚本主要部署在Windows机器上,所以主要以Windows为主,浏览器为C ...
- maven配置文件详解
settings.xml 本篇文章主要对maven中setting.xml配置文件进行解释 1.声明规范 <settings xmlns="http://maven.apache.or ...
- es7重点笔记
1,函数绑定运算符,用来取代call,apply,bind调用,写法:并排的双冒号(::),左边是对象,右边是函数 foo :: bar; // 等同于bar.bind(foo); 双冒号返回的还是原 ...
- 全新的软件项目,好的开始决定了成功一半!(需求&计划)
刚看完“无问西东”,电影里说人总归还是要留下些足迹(文字)的,那么赶紧跑图书馆来留下些文字. 最近去瑞士启动了一个新的项目,那么早上做项目,晚上总结留下了一张张思维导图来记录当时的感受, 手稿如下,字 ...
- Linux - ubuntu中vi不能正常使用方向键与退格键的问题
一度怀疑是键盘坏了! 之前安装solaris也是这个问题! 重新安装vim就可以了! $sudo apt-get remove vim-common $sudo apt-get install vim
- linux如何自动获取ip地址
第一步:激活网卡 系统装好后默认的网卡是eth0,用下面的命令将这块网卡激活. # ifconfig eth0 up 第二步:设置网卡进入系统时启动 想要每次开机就可以自动获取IP地址上网,就要设置网 ...