typedef struct task_node
{
void *arg; /* fun arg. */
void *(*fun) (void *); /* the real work of the task. */
pthread_t tid; /* which thread exec this task. */
int work_id; /* task id. */
int flag; /* 1: assigned, 0: unassigned. */
struct task_node *next;
pthread_mutex_t mutex; /* when modify this ds and exec the work,lock the task ds. */
} TASK_NODE; /*
*the ds of the task_queue
*/
typedef struct task_queue
{
pthread_mutex_t mutex;
pthread_cond_t cond; /* when no task, the manager thread wait for ;when a new task come, signal. */
struct task_node *head; /* point to the task_link. */
int number; /* current number of task, include unassinged and assigned but no finished. */
} TASK_QUEUE_T; /*
*the ds of every thread, make all thread in a double link queue.
*/
typedef struct pthread_node
{
pthread_t tid; /* the pid of this thread in kernel,the value is syscall return . */
int flag; /* 1:busy, 0:free. */
struct task_node *work; /* if exec a work, which work. */
struct pthread_node *next;
struct pthread_node *prev;
pthread_cond_t cond; /* when assigned a task, signal this child thread by manager. */
pthread_mutex_t mutex;
} THREAD_NODE; /*
*the ds of the thread queue
*/
typedef struct pthread_queue
{
int number; /* the number of thread in this queue. */
struct pthread_node *head;
struct pthread_node *rear;
pthread_cond_t cond; /* when no idle thread, the manager wait for ,or when a thread return with idle, signal. */
pthread_mutex_t mutex;
} PTHREAD_QUEUE_T;

四大结构:

1、任务池struct,用于任务的管理,其中的head表示任务队列的第一个元素。内部的cond和mutex用于多线程操作此任务池

2、任务结点task_node,对应于相应的任务,这个任务结点中,包含任务对应的要执行的函数(此函数完成我们真正的socket数据交互等待,write或者read等),完成此任务对应的线程id(用于找到线程),任务分配与否的标志flag,以及操作此任务node的mutex和connd

3、线程池struct pthread_queue,用于管理线程,有线程的首指针和尾指针,线程的个数number,线程池在代码中包括空闲线程和工作线程两大类,分别用两个变量表示

4、线程struc pthread_node,对应线程。包括线程的id,工作状态,此线程要运行的任务task_node指针,用于找到任务,然后 struct pthread_node *next;struct pthread_node *prev形成一个线程结构的双向链表

主程序:

PTHREAD_QUEUE_T * pthread_queue_idle;    /* the idle thread double link queue. */
PTHREAD_QUEUE_T *pthread_queue_busy; /* the work thread double link queue. */
TASK_QUEUE_T *task_queue_head; /* the task queuee single link list. */ int
main (int argc, char *argv[])
{
pthread_t thread_manager_tid, task_manager_tid, monitor_id; init_system (); pthread_create (&thread_manager_tid, NULL, thread_manager, NULL); /* create thread to manage the thread pool. */
pthread_create (&task_manager_tid, NULL, task_manager, NULL); /* create thread recive task from client. */
pthread_create (&monitor_id, NULL, monitor, NULL); /* create thread to monitor the system info. */ pthread_join (thread_manager_tid, NULL);
pthread_join (task_manager_tid, NULL);
pthread_join (monitor_id, NULL); sys_clean (); return ;
}

开了三个线程进行管理,

1、第一个线程是我们的线程分发和任务分发的thread_manager函数对应的线程。

pthread_create (&thread_manager_tid, NULL, thread_manager, NULL);

2、第二个线程是我们进行任务管理的线程,用于处理client新连接,对应产生的新任务,这个线程中,包含着socket创建,bind,listen,accept等过程,然后开始创建task_node结点,并初始化此节点,注册对应的任务工作函数fun,接下来的就是把此任务结点,加入到我们的任务池中,最后的步骤,就是通知我们运行thread_manger函数的线程,任务池有新的任务了,如果你堵塞在这个任务池的connd中,现在可以被唤醒了,接着我们的thread_manger函数对应的线程就可以继续工作了。

 pthread_create (&task_manager_tid, NULL, task_manager, NULL);

我们的thread_manger和task_manager函数的实现:其实thread_manger函数改名为thread_task_dispath更加准确

void *
thread_manager (void *ptr)
{
while ()
{
THREAD_NODE * temp_thread = NULL;
TASK_NODE * temp_task = NULL; /*
*get a new task, and modify the task_queue.
*if no task block om task_queue_head->cond.
*/
pthread_mutex_lock (&task_queue_head->mutex); if (task_queue_head->number == )
pthread_cond_wait (&task_queue_head->cond,
&task_queue_head->mutex); temp_task = task_queue_head->head;
task_queue_head->head = task_queue_head->head->next;
task_queue_head->number--; pthread_mutex_unlock (&task_queue_head->mutex); /*
*get a new idle thread, and modify the idle_queue.
*if no idle thread, block on pthread_queue_idle->cond.
*/
pthread_mutex_lock (&pthread_queue_idle->mutex); if (pthread_queue_idle->number == )
pthread_cond_wait (&pthread_queue_idle->cond,
&pthread_queue_idle->mutex); temp_thread = pthread_queue_idle->head; /*if this is the last idle thread ,modiry the head and rear pointor */
if (pthread_queue_idle->head == pthread_queue_idle->rear)
{
pthread_queue_idle->head = NULL;
pthread_queue_idle->rear = NULL;
}
/*if idle thread number>2, get the first one,modify the head pointor */
else
{
pthread_queue_idle->head = pthread_queue_idle->head->next;
pthread_queue_idle->head->prev = NULL;
} pthread_queue_idle->number--; pthread_mutex_unlock (&pthread_queue_idle->mutex); /*modify the task attribute. */
pthread_mutex_lock (&temp_task->mutex); temp_task->tid = temp_thread->tid;
temp_task->next = NULL;
temp_task->flag = ; pthread_mutex_unlock (&temp_task->mutex); /*modify the idle thread attribute. */
pthread_mutex_lock (&temp_thread->mutex); temp_thread->flag = ;
temp_thread->work = temp_task;
temp_thread->next = NULL;
temp_thread->prev = NULL; pthread_mutex_unlock (&temp_thread->mutex); /*add the thread assinged task to the busy queue. */
pthread_mutex_lock (&pthread_queue_busy->mutex); /*if this is the first one in busy queue */
if (pthread_queue_busy->head == NULL)
{
pthread_queue_busy->head = temp_thread;
pthread_queue_busy->rear = temp_thread;
temp_thread->prev = temp_thread->next = NULL;
}
else
{
/*insert in thre front of the queue */
pthread_queue_busy->head->prev = temp_thread;
temp_thread->prev = NULL;
temp_thread->next = pthread_queue_busy->head;
pthread_queue_busy->head = temp_thread;
pthread_queue_busy->number++;
}
pthread_mutex_unlock (&pthread_queue_busy->mutex); /*signal the child thread to exec the work */
pthread_cond_signal (&temp_thread->cond);
}
}

thread_manger主要过程:

首先从任务池中得到一个任务,如果没有空闲任务,就堵塞,直到我们有新的client连接,产生了一个新的任务,并加入到我们的任务池中,此过程由task_manager对应的线程完成,得到任务之后,我们再通过得到线程池中的空闲线程,如果没有空闲线程就堵塞,如果有空闲线程,首先从空闲线程池中获取首个空闲线程(由双向链表组成的空闲线程),然后修改此空闲线程池。完成之后,修改之前得到的task的属性,包括下面的步骤: 

temp_task->tid = temp_thread->tid;
temp_task->next = NULL;
temp_task->flag = 1;

其中temp是我们得到的任务task_node,把得到的空闲线程的id赋值给我们的task中的tid字段,这样我们的task和我们的刚刚得到空闲线程,就通过tid联系在一起了,同时修改task中的其他字段,例如标志字段flag等等

修改了task属性之后,我们开始修改temp_thread结构(就是得到的空闲线程),包括以下操作:

temp_thread->flag = 1;
temp_thread->work = temp_task;
temp_thread->next = NULL;
temp_thread->prev = NULL;

同理,我们把thread对应的work字段,赋值我们刚才的task,这样在thread和task中,都建立了他们之间的联系。一个线程有自己的任务指针,一个任务同样有一个线程的tid。两者紧密联系起来。

最后我们修改我们的线程池thread_queue,这包括空闲线程池和工作线程池(因为我们声明了两个struct thread_queue的变量),第一个已经修改了空闲线程池,这一步我们修改工作线程池,把这个线程加入工作线程中

pthread_cond_signal (&temp_thread->cond);  //结尾我们通知,堵塞在这里的工作线程work_thread,你们可以开始工作了,因为上面的步骤,就是为工作线程争取任务队列,和空闲线程(上面的空闲线程,其实是空闲线程对应的结构体,但是可以通过或许他们,来表示获取线程,真正的线程其实是运行child_work函数的线程。)

关键点:通过使用thread_node来代表线程,这个结构体虽然不是线程,但是可以通过它,我们进行线程的操作和模拟过程

工作线程 pthread_create (&temp[i].tid, NULL, child_work, (void *) &temp[i]); 这里才是创建工作线程的地方

关键点:我们创建的pthread_node结点是不被free的,就是一旦在初始化函数中,这些结构体被创建,然后这些结构就一直存在,我们后续的线程,会改变这些thread_node结构的内容。

void *
child_work (void *ptr)
{
THREAD_NODE * self = (THREAD_NODE *) ptr; /*modify the tid attribute the first time exec */
pthread_mutex_lock (&self->mutex); self->tid = syscall (SYS_gettid); pthread_mutex_unlock (&self->mutex); while ()
{
pthread_mutex_lock (&self->mutex); /*if no task exec,blocked */
if (NULL == self->work)
{
pthread_cond_wait (&self->cond, &self->mutex);
} pthread_mutex_lock (&self->work->mutex); /*execute the real work. */
self->work->fun (self->work->arg); /*after finished the work */
self->work->fun = NULL;
self->work->flag = ;
self->work->tid = ;
self->work->next = NULL; free (self->work->arg); pthread_mutex_unlock (&self->work->mutex); //unlock the task
pthread_mutex_destroy (&self->work->mutex); /*free the task space */
free (self->work); /*make self thread no work */
self->work = NULL;
self->flag = ; pthread_mutex_lock (&task_queue_head->mutex); /*
*get new task from the task_link if not NULL.
*there no idle thread if there are task to do.
*if on task ,make self idle and add to the idle queue.
*/
if (task_queue_head->head != NULL)
{
TASK_NODE * temp = task_queue_head->head; /*get the first task */
task_queue_head->head = task_queue_head->head->next; /*modify self thread attribute */
self->flag = ;
self->work = temp;
temp->tid = self->tid;
temp->next = NULL;
temp->flag = ; task_queue_head->number--; pthread_mutex_unlock (&task_queue_head->mutex); pthread_mutex_unlock (&self->mutex); continue;
}
else
{
/*no task need to exec, add self to idle queue and del from busy queue */
pthread_mutex_unlock (&task_queue_head->mutex); pthread_mutex_lock (&pthread_queue_busy->mutex); /*self is the last execte thread */
if (pthread_queue_busy->head == self
&& pthread_queue_busy->rear == self)
{
pthread_queue_busy->head = pthread_queue_busy->rear = NULL;
self->next = self->prev = NULL;
} /*the first one thread in busy queue */
else if (pthread_queue_busy->head == self
&& pthread_queue_busy->rear != self)
{
pthread_queue_busy->head = pthread_queue_busy->head->next;
pthread_queue_busy->head->prev = NULL; self->next = self->prev = NULL;
} /*the last one thread in busy queue */
else if (pthread_queue_busy->head != self
&& pthread_queue_busy->rear == self)
{
pthread_queue_busy->rear = pthread_queue_busy->rear->prev;
pthread_queue_busy->rear->next = NULL; self->next = self->prev = NULL;
} /*middle one */
else
{
self->next->prev = self->prev;
self->prev->next = self->next;
self->next = self->prev = NULL;
} pthread_mutex_unlock (&pthread_queue_busy->mutex); /*add self to the idle queue */
pthread_mutex_lock (&pthread_queue_idle->mutex); /*now the idle queue is empty */
if (pthread_queue_idle->head == NULL
|| pthread_queue_idle->head == NULL)
{
pthread_queue_idle->head = pthread_queue_idle->rear = self;
self->next = self->prev = NULL;
} else
{
self->next = pthread_queue_idle->head;
self->prev = NULL;
self->next->prev = self; pthread_queue_idle->head = self;
pthread_queue_idle->number++;
} pthread_mutex_unlock (&pthread_queue_idle->mutex); pthread_mutex_unlock (&self->mutex); /*signal have idle thread */
pthread_cond_signal (&pthread_queue_idle->cond);
}
}
}

这里,我们的工作线程,首先会查看,通过参数传递进来的,线程结构thread_node,是否未被分配任务,通过self-work==NULL.如果已经分配任务,就开始实行任务函数fun,执行完毕之后,在此函数中,修改任务的属性,然后free掉task_node结构体,同时也要进行thread_node属性的修改:

/*make self thread no work */
self->work = NULL;
self->flag = 0;

完成了一个任务之后,如果任务队列中还有任务的话,则直接运行此任务(说明,我们只需要通过thread_manager线程第一次分配任务和线程,一旦得到这个线程,并开始工作之后,我们就可以一直工作,直到工作线程中得不到任务,才等待线程池中的cond

如果工作线程完成一个任务之后,没有新的任务,则我们首先要把此线程加入空闲队列,删除工作队列,然后最后一步就是通知我们的线程管理线程,已经新的空闲线程了。

/*signal have idle thread */

pthread_cond_signal (&pthread_queue_idle->cond);

(这个线程池的实现,并没有动态的增加和删除线程,如果我们当前没有空闲线程,这个就会等待,知道有空闲线程被放进来)


初始化过程:

init_system ();

void  create_pthread_pool (void);

void
create_pthread_pool (void)
{
THREAD_NODE * temp =
(THREAD_NODE *) malloc (sizeof (THREAD_NODE) * THREAD_DEF_NUM); if (temp == NULL)
{
printf (" malloc failure\n");
exit (EXIT_FAILURE);
} /*init as a double link queue */
int i; for (i = ; i < THREAD_DEF_NUM; i++)
{
temp[i].tid = i + ;
temp[i].work = NULL;
temp[i].flag = ; if (i == THREAD_DEF_NUM - )
temp[i].next = NULL; if (i == )
temp[i].prev = NULL; temp[i].prev = &temp[i - ];
temp[i].next = &temp[i + ]; pthread_cond_init (&temp[i].cond, NULL);
pthread_mutex_init (&temp[i].mutex, NULL); /*create this thread */
pthread_create (&temp[i].tid, NULL, child_work, (void *) &temp[i]);
} /*modify the idle thread queue attribute */
pthread_mutex_lock (&pthread_queue_idle->mutex); pthread_queue_idle->number = THREAD_DEF_NUM;
pthread_queue_idle->head = &temp[];
pthread_queue_idle->rear = &temp[THREAD_DEF_NUM - ]; pthread_mutex_unlock (&pthread_queue_idle->mutex);
} /*
*init_system :init the system glob pointor.
*/
void
init_system (void)
{
/*init the pthread_queue_idle */
pthread_queue_idle =
(PTHREAD_QUEUE_T *) malloc (sizeof (PTHREAD_QUEUE_T)); pthread_queue_idle->number = ;
pthread_queue_idle->head = NULL;
pthread_queue_idle->rear = NULL;
pthread_mutex_init (&pthread_queue_idle->mutex, NULL);
pthread_cond_init (&pthread_queue_idle->cond, NULL); /*init the pthread_queue_busy */
pthread_queue_busy =
(PTHREAD_QUEUE_T *) malloc (sizeof (PTHREAD_QUEUE_T)); pthread_queue_busy->number = ;
pthread_queue_busy->head = NULL;
pthread_queue_busy->rear = NULL;
pthread_mutex_init (&pthread_queue_busy->mutex, NULL);
pthread_cond_init (&pthread_queue_busy->cond, NULL); /*init the task_queue_head */
task_queue_head = (TASK_QUEUE_T *) malloc (sizeof (TASK_QUEUE_T)); task_queue_head->head = NULL;
task_queue_head->number = ;
pthread_cond_init (&task_queue_head->cond, NULL);
pthread_mutex_init (&task_queue_head->mutex, NULL); /*create thread poll */
create_pthread_pool ();
}

重点是创建我们的工作线程work_thread,

 for (i = ; i < THREAD_DEF_NUM; i++)
{
temp[i].tid = i + ;
temp[i].work = NULL;
temp[i].flag = ; if (i == THREAD_DEF_NUM - )
temp[i].next = NULL; if (i == )
temp[i].prev = NULL; temp[i].prev = &temp[i - ];
temp[i].next = &temp[i + ]; pthread_cond_init (&temp[i].cond, NULL);
pthread_mutex_init (&temp[i].mutex, NULL); /*create this thread */
pthread_create (&temp[i].tid, NULL, child_work, (void *) &temp[i]);
}

这里我们创建了我们的工作线程,还有也创建了与工作线程对于的thread_node结构。这样我们就可以通过这个结构去代表我们的线程,这个结构从这里创建之后,之后只会被修改,但是不会free,而我们的task_node则在child_work()函数中被free掉。

结构框架:

运行三个管理管控线程,thread_manager,task_mangager,和monitor.

初始化若干个工作线程,同时初始化线程池结构和任务池结构task_quque,thread_queue

linux线程池的更多相关文章

  1. linux线程池thrmgr源码解析

    linux线程池thrmgr源码解析 1         thrmgr线程池的作用 thrmgr线程池的作用是提高程序的并发处理能力,在多CPU的服务器上运行程序,可以并发执行多个任务. 2      ...

  2. 一个简单的linux线程池(转-wangchenxicool)

    线程池:简单地说,线程池 就是预先创建好一批线程,方便.快速地处理收到的业务.比起传统的到来一个任务,即时创建一个线程来处理,节省了线程的创建和回收的开销,响应更快,效率更高. 在linux中,使用的 ...

  3. 非常精简的Linux线程池实现(一)——使用互斥锁和条件变量

    线程池的含义跟它的名字一样,就是一个由许多线程组成的池子. 有了线程池,在程序中使用多线程变得简单.我们不用再自己去操心线程的创建.撤销.管理问题,有什么要消耗大量CPU时间的任务通通直接扔到线程池里 ...

  4. Linux线程池的实现

    线程池的实现 1:自定义封装的条件变量 //condition.h #ifndef _CONDITION_H_ #define _CONDITION_H_ #include <pthread.h ...

  5. Linux线程池在server上简单应用

    一.问题描写叙述 如今以C/S架构为例.client向server端发送要查找的数字,server端启动线程中的线程进行对应的查询.将查询结果显示出来. 二.实现方案 1. 整个project以cli ...

  6. Linux下简易线程池

    线程池简介 线程池是可以用来在后台执行多个任务的线程集合. 这使主线程可以自由地异步执行其他任务.线程池通常用于服务器应用程序. 每个传入请求都将分配给线程池中的一个线程,因此可以异步处理请求,而不会 ...

  7. 线程池(Linux实现)

    讨论QQ群:135202158 本文技术参考了sourceforge项目c thread pool,链接:http://sourceforge.net/projects/cthpool/ 线程池如上一 ...

  8. Linux平台下线程池的原理及实现

    转自:http://blog.csdn.net/lmh12506/article/details/7753952 前段时间在github上开了个库,准备实现自己的线程池的,因为换工作的事,一直也没有实 ...

  9. 高效线程池之无锁化实现(Linux C)

    from:http://blog.csdn.net/xhjcehust/article/details/45844901 笔者之前练手写过一个小的线程池版本(已上传至https://github.co ...

随机推荐

  1. 2017年总结&2018年计划

    谈一谈2017年计划: 1.完成壁咚项目2.写一个自己的扫描器3.完善web安全手册.4.搞一个大漏洞或CVE的漏洞 完成进度:1.壁咚这个项目,当初发誓要用java来写完,其实最开始就已经写完了,前 ...

  2. 2015.5.9 C#编写DLL及C#调用C#DLL

    过程比C#调用VC++dll简单. 一.创建DLL 新建工程,类型选择类库,生成的结果就是dll 注意:在项目属性-应用程序中,注意三个地方,程序集名称和默认命名空间可以调整,但要一致,别的程序调用此 ...

  3. js中substring和substr用法与区别

    String.substring( ):用于返回一个字符串的子串 用法如下:string.substring(from, to) 其中from指代要抽去的子串第一个字符在原字符串中的位置 to指代所要 ...

  4. DAY9-python并发之多线程理论

    一 什么是线程 在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程 线程顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程 车间负责把资源整合 ...

  5. ubuntu系统里vi编辑器时,按方向箭头输入是乱码的ABCD字母?(图文详解)

    不多说,直接上干货! 问题详情 ubuntu系统里vi编辑器时,按方向箭头输入是乱码的ABCD字母?  解决办法 是由于预装的vim软件没更新,运行   sudo apt-get install vi ...

  6. Http 与Https

    一个Http请求 DNS域名解析 --> 发起TCP的三次握手 --> 建立TCP连接后发起http请求 --> 服务器响应http请求,浏览器得到html代码 --> 浏览器 ...

  7. js实现导航栏的吸顶操作

    <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"% ...

  8. JavaScript基础笔记集合(转)

    JavaScript基础笔记集合   JavaScript基础笔记集合   js简介 js是脚本语言.浏览器是逐行的读取代码,而传统编程会在执行前进行编译   js存放的位置 html脚本必须放在&l ...

  9. Contset Hunter 1102 高精度求卡特兰数

    用递推的方式写的写挂了,而如果用组合数又不会高精度除法,偶然看到了别人的只用高精度乘低精度求组合数的方法,记录一下 #include<bits/stdc++.h> using namesp ...

  10. Linux共享对象之编译参数 -fPIC

    转载自:https://www.cnblogs.com/cswuyg/p/3830703.html     在Linux系统中,动态链接文件称为动态共享对象(DSO,Dynamic Shared Ob ...