用条件变量和互斥锁去管理线程池
Posted 西邮Linux兴趣小组
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用条件变量和互斥锁去管理线程池相关的知识,希望对你有一定的参考价值。
今天终于克服众多Bug 搞出了一个简单的线程池的应用(思想来自一篇外文Blog),在初始化线程池之后,可以实现向其中投放任务,多个线程完成大于线程数目的任务量,当然每个任务只能一次被一个线程执行。当一个线程完成一个任务后回去检测当前的剩余任务,从而继续执行剩余任务。大体的内容实现就是这样,下面仔细说一下整个流程问题。
首先谈到线程池,顾名思义我们需要预先建立多个线程,但是怎么去管理这些线程才是重点。下面的结构可以作为线程池的一个管理结构:
其中的条件变量和互斥锁是为了后来多个线程访问任务所需要用到的控制条件。threadid_link 是一个pthread_t 类型的链,用来管理线程ID,max_thread_num是当前线程池可以产生的最大线程数量,current_queue_size 是当前线程池中还未处理的任务数量。destory_flag 是一个判断标记,检查是否需要销毁线程池。其中的task_head 是一个Work_task类型的指针,用来管理对应投放到当前线程池的任务链,可以用下面的结构表示一个任务:
Task_process 是一个参数为arg的函数指针,next为链表的next域指针。
这样我们表示了一个线程池的管理结构,下面我们就需要去初始化这个线程池,开始我们定义一个全局的线程池结构的变量。
初始化我们的线程池结构其实是对thread_pool的数据进行初始化,设定条件变量,互斥锁之类的以及最重要的建立指定数量max_thread_num个线程:
上面初始化线程池时,创建线程就需要让线程去调用对应的事物函数thread_routine(),在这里面我们首先需要检测当前线程池中的任务数量(没有任务下,当前对应的线程就需要等待,通过条件变量来控制,当下一次投放任务进来时会调用pthread_cond_signal唤醒这个线程),还有当前线程池所处的一个状态,是否需要销毁,执行对应的操作,当然这只是预先判断,下来我们要做的就是让当前的可用线程执行对应的任务,我们之前说过,线程池中的所有任务都存放在task_head所指向的一条任务链(函数链表)里面,每次通过加锁机制保证当前线程取得的是task_head这条链中的第一个任务,然后执行对应的任务。
之后需要释放当前的任务。当然需要对线程池中的任务数量做出调整(减1)。对应的thread_routine()是:
在此,我们通过互斥锁的机制保证了线程的任务执行流程,现在我们关心的是既然线程需要执行任务,那么怎么向线程池中投放任务,即我们需要一个函数实现任务投放,我们可以声明一个函数:
我们之前说过线程池中的任务管理是通过一个Work_task类型的单链表实现的,我们每次去遍历这条任务链。过程其实就是建立一条单链表,将Add_task_to_threadpool()对应的task_process函数传递进去给单链表的一个节点。在这里,我使用尾插。完成后更新对应的线程池信息,最重要的是最后的pthread_cond_signal(),唤醒当前pthread_cond_wait阻塞的线程。
到这里,我们完成了对线程池的任务投放,所需要的只是上面Add_task_to_threadpool()所需要的任务了,即task_process,这只是一个小小的任务测试函数,我们只去执行简单的打印操作(对应的线程id和任务id)便可。
在多个线程处理完大于线程数目的任务量时(一个线程一次处理一个任务),我们需要的就是资源的回收和善后操作,阻塞所有的线程,销毁线程池,销毁任务队列,销毁条件变量和互斥锁。
在此我们一个线程池从开始创建到最后的销毁以及中间的管理和运行流程就是这样。我们用这样的main函数来测试我们的线程池:
测试运行结果如下 :
一个简单的通过条件变量与加锁机制的线程池管理和执行任务操作的过程就是这样。但是其中涉及到的东西其实还是蛮复杂的,我们通过投放任务再以条件变量的形式唤醒等待线程。包括这种线程池的管理结构其实都是挺不错的思想。不多说了,写到这手都酸了。Over !
https://blog.csdn.net/sim_szm/article/details/9843183
(更多详细内容,点击左下角【阅读原文】)
以上是关于用条件变量和互斥锁去管理线程池的主要内容,如果未能解决你的问题,请参考以下文章