Linux下简单的多线程编程--线程池的实现

/*  写在前面的话:

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

    选择在这个特殊的时候注册并发文章也是有一些我个人特殊的意义的,看我的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。所以,线程池最大的优势就是节省了线程申请以及销毁的时间!

  下面是具体的代码实现:

  

/*************************************************************************
> 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;
}

时间: 2024-10-10 15:11:14

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

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

互斥锁通信机制 基本原理 互斥锁以排他方式防止共享数据被并发访问,互斥锁是一个二元变量,状态为开(0)和关(1),将某个共享资源与某个互斥锁逻辑上绑定之后,对该资源的访问操作如下: (1)在访问该资源之前需要首先申请互斥锁,如果锁处于开状态,则申请得到锁并立即上锁(关),防止其他进程访问资源,如果锁处于关,则默认阻塞等待. (2)只有锁定该互斥锁的进程才能释放该互斥锁. 互斥量类型声明为pthread_mutex_t数据类型,在<bits/pthreadtypes.h>中有具体的定义. 互斥量

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

学东西,往往实例才是最让人感兴趣的,老是学基础理论,不动手,感觉没有成就感,呵呵.下面先来一个实例.我们通过创建两个线程来实现对一个数的递加.或许这个实例没有实际运用的价值,但是稍微改动一下,我们就可以用到其他地方去拉.下面是我们的代码: /*thread_example.c : c multiple thread programming in linux *author : falcon *E-mail : [email protected] */ #include <pthread.h>

多线程编程—线程池的实现

执行与任务分离的组件- 线程池 多线程技术主要解决了处理器单元内多个线程执行的问题,它可以显著的减少处理器单元的闲置时间,增加处理器单元的吞吐能力.线程池是多线程编程的一个必要组件,并且对于很多编程人员都是透明的,更是神秘的.有幸能为大家解析其中缘由,尚有不妥之处,欢迎大家抛砖. 线程池的概念,是一个用来管理一组执行任务线程的工具.既然是管理工具,那么该工具管理是用来管理任务与执行的.如图一线程池组件拓扑图,执行队列(Workers),任务队列(Jobs)和池管理(Pool Manager)三部

Java 线程池和多线程编程 ——线程池理解与创建

JDK1.5 引入了 Executor框架 ,对任务提交和执行进行解耦 , 定义任务后交由线程池执行. 线程池是由java.util.concurrent 包中Executors类的工厂方法创建线程池. -------------------------------------------------------------------------------- 创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程. public static ExecutorService n

linux下的多进程,多线程编程

关于多进程和多线程,一直想写点什么来进行一次总结,今天终于提笔了,若有讲解错误之处,希望广大读者能给予指正.,我想从以下几个方面进行一次详解划分.第一,运用.第二,同步.第三,通信.第四,选择. 那么闲话少说,开始第一个,关于线程和进程之间的运用. 什么是进程? 有一个很官方的说法:进程是程序在计算机上的一次执行活动.但我觉得,可能这句话有点不对,应该换成进程加线程是程序在计算机上的一次执行活动才更加的合理.因为进程是资源分配的最小单位,线程是CPU调度的最小单位.所以我们看到进程的时候,应该联

linux下简单socket网络编程

在进行socket网络编程时, 我们需要了解一些必备的知识,例如什么是socket,ipv4的地址结构,套接字类型等等,不然上来直接看代码就会晕,当初学习网络编程时,看书上的例子,总有感觉书上讲的都很简要.再或者讲的原理太多把人绕晕.我这里只想让大家简单知道怎么使用socket进行网络编程并且给出的例子可以直接使用参考. 1. 什么是socket (1) socket 可以看成是用户进程与网络协议栈的编程接口.就是说应用层可以看成是用户进程,传输层网络层数据链路层看成网络协议栈,因为这三个层的传

Linux下C语言多线程,网络通信简单聊天程序

原文:Linux下C语言多线程,网络通信简单聊天程序 功能描述:程序应用多线程技术,可是实现1对N进行网络通信聊天.但至今没想出合适的退出机制,除了用Ctr+C.出于演示目的,这里采用UNIX域协议(文件系统套接字),程序分为客户端和服务端.应用select函数来实现异步的读写操作. 先说一下服务端:首先先创建套接字,然后绑定,接下进入一个无限循环,用accept函数,接受“连接”请求,然后调用创建线程函数,创造新的线程,进入下一个循环.这样每当有一个新的“连接”被接受都会创建一个新的线程,实现

Linux程序设计学习笔记----多线程编程之线程同步之条件变量

转载请注明出处:http://blog.csdn.net/suool/article/details/38582521. 基本概念与原理 互斥锁能够解决资源的互斥访问,但是在某些情况下,互斥并不能解决问题,比如两个线程需 要互斥的处理各自的操作,但是一个线程的操作仅仅存在一种条件成立的情况下执行,一旦错过不可再重现,由于线程间相互争夺cpu资源,因此在条件成立的时候,该线程不一定争夺到cpu而错过,导致永远得不到执行..... 因此需要某个机制来解决此问题,更重要的是,线程仅仅只有一种情况需要执

Linux程序设计学习笔记----多线程编程基础概念与基本操作

转载请注明出处,http://blog.csdn.net/suool/article/details/38542543,谢谢. 基本概念 线程和进程的对比 用户空间资源对比 每个进程在创建的时候都申请了新的内存空间以存储代码段\数据段\BSS段\堆\栈空间,并且这些的空间的初始化值是父进程空间的,父子进程在创建后不能互访资源. 而每个新创建的线程则仅仅申请了自己的栈,空间,与同进程的其他线程共享该进程的其他数据空间包括代码段\数据段\BSS段\堆以及打开的库,mmap映射的文件与共享的空间,使得